Skip to content

Commit 63155f8

Browse files
authored
chore: deno and jsr
2 parents 503a136 + c5bb2aa commit 63155f8

File tree

10 files changed

+294
-164
lines changed

10 files changed

+294
-164
lines changed

CONTRIBUTING.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Contributing
2+
3+
Please make a Github issue before making any pull requests.
4+
5+
# Bugs
6+
7+
Any bugs should be attached to a Github issue.
8+
9+
# Style
10+
11+
Please follow the existing styling of the repository, and include documentation
12+
on new code.

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1-
# rcon
1+
# Deno Source RCON Protocol
22

3+
Complete implementation of the
4+
[Source RCON Protocol](https://developer.valvesoftware.com/wiki/Source_RCON_Protocol).
35

6+
## Install
7+
8+
Checkout the [jsr page](https://jsr.io/@c43721/rcon) for more details.
9+
10+
### Examples
11+
12+
For more examples, see the [documentation on jsr](https://jsr.io/@c43721/rcon/doc).
13+
14+
## Contributing
15+
16+
If there's a feature or bug, please raise a github issue first alongside your PR
17+
(if you're kind enough to make a PR.)
18+
19+
## Acknowledgements
20+
21+
- EnriqCG's [rcon-srcds](https://github.com/EnriqCG/rcon-srcds)
22+
- ribizli's [deno_rcon](https://github.com/ribizli/deno_rcon)
23+
24+
Both of these repositories I've contributed to in the past and am super thankful for their work.
25+
26+
## License
27+
28+
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.

cli.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { parseArgs } from "@std/cli";
2+
import Rcon from "./src/rcon.ts";
3+
4+
const args = parseArgs(Deno.args, {
5+
string: ["password", "ip", "port", "command"],
6+
boolean: ["console", "file"],
7+
});
8+
9+
if (!args.password || !args.ip || !args.command) {
10+
console.error("Must specify a password, ip and command");
11+
} else {
12+
const port = parseInt(args.port ?? "27015", 10);
13+
14+
using rcon = new Rcon({
15+
host: args.ip,
16+
port,
17+
});
18+
19+
const didAuthenticate = await rcon.authenticate(args.password!);
20+
21+
if (didAuthenticate) {
22+
const result = await rcon.execute(args.command!);
23+
24+
if (args.file) {
25+
Deno.writeTextFile(`${Deno.cwd()}/rcon-result.txt`, result.toString(), {
26+
append: true,
27+
});
28+
} else if (args.console) {
29+
console.log(result);
30+
}
31+
} else {
32+
console.error("RCON password incorrect");
33+
}
34+
}

deno.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"name": "@c43721/rcon",
33
"imports": {
4+
"@std/bytes": "jsr:@std/bytes@^1.0.2",
5+
"@std/cli": "jsr:@std/cli@^1.0.6",
46
"@std/io": "jsr:@std/io@^0.225.0"
57
},
68
"version": "0.0.1-alpha1",

deno.lock

Lines changed: 25 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/errors.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,23 @@ export class NotAuthorizedException extends Error {
1919
}
2020
}
2121

22+
export class NotConnectedException extends Error {
23+
constructor() {
24+
super();
25+
this.message = "Not connected";
26+
}
27+
}
28+
29+
export class UnableToParseResponseException extends Error {
30+
constructor() {
31+
super();
32+
this.message = "Unable to parse response";
33+
}
34+
}
35+
2236
export class PacketSizeTooBigException extends Error {
2337
constructor() {
2438
super();
2539
this.message = "Packet size too big";
2640
}
2741
}
28-

src/packet.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
import { Buffer } from "node:buffer";
2-
31
/**
4-
* Encode data to packet buffer
2+
* Encodes data to packet buffer
53
* @param type Packet Type
64
* @param id Packet ID
75
* @param body Packet body (payload)
8-
* @param encoding Body encoding
96
* @returns Encoded packet buffer
107
*/
11-
export const encode = (type: number, id: number, body: string): Buffer => {
12-
const size = Buffer.byteLength(body) + 14; // body size + 10 + 4 (Null)
13-
const buffer = Buffer.alloc(size);
8+
export const encode = (type: number, id: number, body: string): Uint8Array => {
9+
const dataBuffer = new TextEncoder().encode(body);
10+
const dataLength = dataBuffer.length;
11+
12+
const sendBuffer = new Uint8Array(dataLength + 14);
13+
const view = new DataView(sendBuffer.buffer);
1414

15-
buffer.writeInt32LE(size - 4, 0);
16-
buffer.writeInt32LE(id, 4);
17-
buffer.writeInt32LE(type, 8);
18-
buffer.write(body, 12, size - 2);
19-
buffer.writeInt16LE(0, size - 2);
15+
view.setInt32(0, dataLength + 10, true);
16+
view.setInt32(4, id, true);
17+
view.setInt32(8, type, true);
18+
// set the text data
19+
sendBuffer.set(dataBuffer, 12);
20+
view.setInt16(dataLength + 12, 0, true);
2021

21-
return buffer;
22+
return sendBuffer;
2223
};
2324

2425
/**
@@ -27,15 +28,19 @@ export const encode = (type: number, id: number, body: string): Buffer => {
2728
* @param encoding Body encoding
2829
* @returns Decoded packet object
2930
*/
30-
export const decode = (
31-
buf: Buffer,
32-
encoding: EncodingOptions = "ascii"
33-
): DecodedPacket => {
31+
export const decode = (data: Uint8Array): DecodedPacket => {
32+
const dataView = new DataView(data.buffer);
33+
34+
const size = dataView.getInt32(0, true);
35+
const id = dataView.getInt32(4, true);
36+
const type = dataView.getInt32(8, true);
37+
const payload = data.slice(12, 12 + size - 10);
38+
3439
return {
35-
size: buf.readInt32LE(0),
36-
id: buf.readInt32LE(4),
37-
type: buf.readInt32LE(8),
38-
body: buf.toString(encoding, 12, buf.byteLength - 2),
40+
size,
41+
id,
42+
type,
43+
body: new TextDecoder().decode(payload),
3944
};
4045
};
4146

@@ -45,5 +50,3 @@ interface DecodedPacket {
4550
type: number;
4651
body: string;
4752
}
48-
49-
export type EncodingOptions = "ascii" | "utf8";

src/protocol.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,44 @@
11
/**
2-
* RCON Packet Types
3-
* Reference: https://developer.valvesoftware.com/wiki/Source_RCON#Requests_and_Responses
2+
* The list of [RCON Packet Types](https://developer.valvesoftware.com/wiki/Source_RCON#Requests_and_Responses)
43
*
54
* @readonly
65
*/
76
const protocol = {
87
/**
9-
* First packet for authentication
8+
* Used for authenticating the connection with the server
109
*/
1110
SERVERDATA_AUTH: 0x03,
1211

1312
/**
14-
* Command issued to the server
13+
* Represents a command issue to the server by a client
1514
*/
1615
SERVERDATA_EXECCOMMAND: 0x02,
1716

1817
/**
19-
* Response of SERVERDATA_AUTH
20-
* @remarks If body is -1, the auth failed
18+
* Notifies the connection's current authentication status
19+
*
20+
* @remarks When sent, the server will respond with an empty {@link SERVERDATA_RESPONSE_VALUE} followed by a {@link SERVERDATA_AUTH_RESPONSE} indicating if the authentication was successful. A value of -1 for the packet id will be set if the authentication failed
2121
*/
2222
SERVERDATA_AUTH_RESPONSE: 0x02,
2323

2424
/**
25-
* Response of SERVERDATA_EXECCOMMAND
25+
* Response to a {@link SERVERDATA_EXECCOMMAND}
2626
*/
2727
SERVERDATA_RESPONSE_VALUE: 0x00,
2828

29+
/**
30+
* The packet id used when issuing {@link SERVERDATA_AUTH} commands
31+
*
32+
* @internal
33+
*/
2934
ID_AUTH: 0x999,
3035

31-
ID_REQUEST: 0x123,
32-
33-
ID_TERM: 0x777,
36+
/**
37+
* The packet id used when working with multipacket responses
38+
*
39+
* @internal
40+
*/
41+
ID_TERM: 0x888,
3442
} as const;
3543

3644
export default protocol;

0 commit comments

Comments
 (0)