baltica 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge/bridge-options.d.ts +22 -0
- package/dist/bridge/bridge-options.js +11 -0
- package/dist/bridge/bridge-player.d.ts +14 -0
- package/dist/bridge/bridge-player.js +26 -0
- package/dist/bridge/bridge.d.ts +25 -0
- package/dist/bridge/bridge.js +143 -0
- package/dist/client/client-data.d.ts +28 -0
- package/dist/client/client-data.js +140 -0
- package/dist/client/client-options.d.ts +58 -0
- package/dist/client/client-options.js +48 -0
- package/dist/client/client.d.ts +44 -0
- package/dist/client/client.js +216 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +21 -0
- package/dist/client/types/index.d.ts +3 -0
- package/dist/client/types/index.js +19 -0
- package/dist/client/types/login-data.d.ts +11 -0
- package/dist/client/types/login-data.js +26 -0
- package/dist/client/types/payload.d.ts +50 -0
- package/dist/client/types/payload.js +91 -0
- package/dist/client/types/skin/Skin.json +112 -0
- package/dist/client/types/skin/index.d.ts +1 -0
- package/dist/client/types/skin/index.js +17 -0
- package/dist/client/worker/WorkerClient.d.ts +18 -0
- package/dist/client/worker/WorkerClient.js +99 -0
- package/dist/client/worker/index.d.ts +2 -0
- package/dist/client/worker/index.js +18 -0
- package/dist/client/worker/worker.d.ts +4 -0
- package/dist/client/worker/worker.js +131 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -0
- package/dist/libs/emitter.d.ts +13 -0
- package/dist/libs/emitter.js +76 -0
- package/dist/libs/index.d.ts +1 -0
- package/dist/libs/index.js +17 -0
- package/dist/network/auth.d.ts +17 -0
- package/dist/network/auth.js +148 -0
- package/dist/network/client-cache-status.d.ts +8 -0
- package/dist/network/client-cache-status.js +37 -0
- package/dist/network/index.d.ts +3 -0
- package/dist/network/index.js +19 -0
- package/dist/network/level-chunk-packet.d.ts +23 -0
- package/dist/network/level-chunk-packet.js +75 -0
- package/dist/network/packet-compressor.d.ts +13 -0
- package/dist/network/packet-compressor.js +82 -0
- package/dist/network/packet-encryptor.d.ts +21 -0
- package/dist/network/packet-encryptor.js +101 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +19 -0
- package/dist/server/player.d.ts +34 -0
- package/dist/server/player.js +166 -0
- package/dist/server/server-options.d.ts +27 -0
- package/dist/server/server-options.js +16 -0
- package/dist/server/server.d.ts +17 -0
- package/dist/server/server.js +44 -0
- package/dist/tools/bridge.d.ts +1 -0
- package/dist/tools/bridge.js +35 -0
- package/dist/tools/client.d.ts +1 -0
- package/dist/tools/client.js +59 -0
- package/dist/tools/server.d.ts +1 -0
- package/dist/tools/server.js +8 -0
- package/package.json +36 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as Protocol from "@serenityjs/protocol";
|
|
2
|
+
import type { PacketNames } from "../client";
|
|
3
|
+
import type { ClientCacheStatusPacket } from "../network/client-cache-status";
|
|
4
|
+
import { type ServerOptions } from "../server/server-options";
|
|
5
|
+
export type BridgePlayerEvents = {
|
|
6
|
+
[K in PacketNames as `clientbound-${K}`]: [
|
|
7
|
+
packet: InstanceType<(typeof Protocol)[K]>
|
|
8
|
+
];
|
|
9
|
+
} & {
|
|
10
|
+
[K in PacketNames as `serverbound-${K}`]: [
|
|
11
|
+
packet: InstanceType<(typeof Protocol)[K]>
|
|
12
|
+
];
|
|
13
|
+
} & {
|
|
14
|
+
"serverbound-ClientCacheStatusPacket": [packet: ClientCacheStatusPacket];
|
|
15
|
+
};
|
|
16
|
+
export type BridgeOptions = ServerOptions & {
|
|
17
|
+
destination: {
|
|
18
|
+
host: string;
|
|
19
|
+
port: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export declare const defaultBridgeOptions: BridgeOptions;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultBridgeOptions = void 0;
|
|
4
|
+
const server_options_1 = require("../server/server-options");
|
|
5
|
+
exports.defaultBridgeOptions = {
|
|
6
|
+
...server_options_1.defaultServerOptions,
|
|
7
|
+
destination: {
|
|
8
|
+
host: "127.0.0.1",
|
|
9
|
+
port: 19132,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Client } from "../client";
|
|
2
|
+
import { Emitter } from "../libs";
|
|
3
|
+
import type { Player } from "../server";
|
|
4
|
+
import type { Bridge } from "./bridge";
|
|
5
|
+
import type { BridgePlayerEvents } from "./bridge-options";
|
|
6
|
+
export declare class BridgePlayer extends Emitter<BridgePlayerEvents> {
|
|
7
|
+
player: Player;
|
|
8
|
+
bridge: Bridge;
|
|
9
|
+
client: Client;
|
|
10
|
+
cacheStatus: boolean;
|
|
11
|
+
constructor(bridge: Bridge, player: Player);
|
|
12
|
+
prepare(): void;
|
|
13
|
+
getClient(): Client;
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BridgePlayer = void 0;
|
|
4
|
+
const protocol_1 = require("@serenityjs/protocol");
|
|
5
|
+
const libs_1 = require("../libs");
|
|
6
|
+
class BridgePlayer extends libs_1.Emitter {
|
|
7
|
+
constructor(bridge, player) {
|
|
8
|
+
super();
|
|
9
|
+
this.bridge = bridge;
|
|
10
|
+
this.player = player;
|
|
11
|
+
}
|
|
12
|
+
prepare() {
|
|
13
|
+
this.player.connection.on("disconnect", () => {
|
|
14
|
+
const disconnect = new protocol_1.DisconnectPacket();
|
|
15
|
+
disconnect.hideDisconnectScreen = false;
|
|
16
|
+
disconnect.message = new protocol_1.DisconnectMessage("");
|
|
17
|
+
disconnect.reason =
|
|
18
|
+
protocol_1.DisconnectReason.UnspecifiedClientInstanceDisconnection;
|
|
19
|
+
this.client.send(disconnect);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
getClient() {
|
|
23
|
+
return this.client;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.BridgePlayer = BridgePlayer;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ForceArray } from "../libs";
|
|
2
|
+
import { type Player, Server, type ServerEvents } from "../server";
|
|
3
|
+
import { type BridgeOptions } from "./bridge-options";
|
|
4
|
+
import { BridgePlayer } from "./bridge-player";
|
|
5
|
+
type BridgeSpecificEvents = {
|
|
6
|
+
connect: [BridgePlayer];
|
|
7
|
+
};
|
|
8
|
+
type BridgeEvents = ServerEvents & BridgeSpecificEvents;
|
|
9
|
+
export declare class Bridge extends Server {
|
|
10
|
+
options: BridgeOptions;
|
|
11
|
+
private clients;
|
|
12
|
+
private packetClassCache;
|
|
13
|
+
private readonly debugLog;
|
|
14
|
+
constructor(options?: Partial<BridgeOptions>);
|
|
15
|
+
private initializePacketCache;
|
|
16
|
+
private getPacketClass;
|
|
17
|
+
private processPacketCommon;
|
|
18
|
+
prepare(): void;
|
|
19
|
+
onConnect(player: Player): void;
|
|
20
|
+
onLogin(player: BridgePlayer): void;
|
|
21
|
+
emit: <K extends keyof BridgeEvents>(name: K, ...args: ForceArray<BridgeEvents[K]>) => void;
|
|
22
|
+
on: <K extends keyof BridgeEvents>(name: K, callback: (...args: ForceArray<BridgeEvents[K]>) => void) => void;
|
|
23
|
+
once: <K extends keyof BridgeEvents>(name: K, callback: (...args: ForceArray<BridgeEvents[K]>) => void) => void;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Bridge = void 0;
|
|
4
|
+
const raknet_1 = require("@sanctumterra/raknet");
|
|
5
|
+
const protocol_1 = require("@serenityjs/protocol");
|
|
6
|
+
const client_1 = require("../client");
|
|
7
|
+
const client_cache_status_1 = require("../network/client-cache-status");
|
|
8
|
+
const level_chunk_packet_1 = require("../network/level-chunk-packet");
|
|
9
|
+
const server_1 = require("../server");
|
|
10
|
+
const bridge_options_1 = require("./bridge-options");
|
|
11
|
+
const bridge_player_1 = require("./bridge-player");
|
|
12
|
+
class Bridge extends server_1.Server {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
super(options);
|
|
15
|
+
this.clients = new Map();
|
|
16
|
+
this.packetClassCache = new Map();
|
|
17
|
+
this.debugLog = false;
|
|
18
|
+
this.options = { ...bridge_options_1.defaultBridgeOptions, ...options };
|
|
19
|
+
this.prepare();
|
|
20
|
+
this.initializePacketCache();
|
|
21
|
+
}
|
|
22
|
+
initializePacketCache() {
|
|
23
|
+
const CLIENT_CACHE_STATUS_ID = 129;
|
|
24
|
+
this.packetClassCache.set(CLIENT_CACHE_STATUS_ID, client_cache_status_1.ClientCacheStatusPacket);
|
|
25
|
+
if ("id" in level_chunk_packet_1.LevelChunkPacket) {
|
|
26
|
+
const levelChunkId = level_chunk_packet_1.LevelChunkPacket.id;
|
|
27
|
+
this.packetClassCache.set(levelChunkId, level_chunk_packet_1.LevelChunkPacket);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
getPacketClass(id, PacketClass) {
|
|
31
|
+
const CLIENT_CACHE_STATUS_ID = 129;
|
|
32
|
+
let CachedPacketClass = this.packetClassCache.get(id);
|
|
33
|
+
if (!CachedPacketClass) {
|
|
34
|
+
if (id === CLIENT_CACHE_STATUS_ID) {
|
|
35
|
+
CachedPacketClass =
|
|
36
|
+
client_cache_status_1.ClientCacheStatusPacket;
|
|
37
|
+
}
|
|
38
|
+
else if (PacketClass && typeof PacketClass === "function") {
|
|
39
|
+
CachedPacketClass = PacketClass;
|
|
40
|
+
}
|
|
41
|
+
if (CachedPacketClass) {
|
|
42
|
+
this.packetClassCache.set(id, CachedPacketClass);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return CachedPacketClass;
|
|
46
|
+
}
|
|
47
|
+
processPacketCommon(buffer, player, isClientbound, sender) {
|
|
48
|
+
const CLIENT_CACHE_STATUS_ID = 129;
|
|
49
|
+
const id = (0, protocol_1.getPacketId)(buffer);
|
|
50
|
+
const PacketClass = protocol_1.Packets[id];
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
if (!PacketClass && id !== CLIENT_CACHE_STATUS_ID) {
|
|
53
|
+
sender.send(buffer);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const packetName = PacketClass?.name ?? "ClientCacheStatusPacket";
|
|
57
|
+
const eventName = `${isClientbound ? "clientbound" : "serverbound"}-${packetName}`;
|
|
58
|
+
if (!player.hasListeners(eventName) &&
|
|
59
|
+
packetName !== "ClientCacheStatusPacket") {
|
|
60
|
+
sender.send(buffer);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const CachedPacketClass = this.getPacketClass(id, PacketClass);
|
|
65
|
+
if (!CachedPacketClass) {
|
|
66
|
+
sender.send(buffer);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (this.debugLog) {
|
|
70
|
+
raknet_1.Logger.info(`${isClientbound ? "Client -> BridgePlayer" : "BridgePlayer -> Client"} : ${packetName}`);
|
|
71
|
+
}
|
|
72
|
+
const packet = new CachedPacketClass(buffer).deserialize();
|
|
73
|
+
if (packet instanceof client_cache_status_1.ClientCacheStatusPacket) {
|
|
74
|
+
packet.supported = false;
|
|
75
|
+
raknet_1.Logger.warn("Ignoring ClientCacheStatusPacket");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
player.emit(eventName, packet);
|
|
79
|
+
if ("binary" in packet) {
|
|
80
|
+
packet.binary = [];
|
|
81
|
+
}
|
|
82
|
+
const newBuffer = packet.serialize();
|
|
83
|
+
sender.send(buffer.equals(newBuffer) ? buffer : newBuffer);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
console.error(`Failed to process ${packetName}`, e);
|
|
87
|
+
sender.send(buffer);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
prepare() {
|
|
91
|
+
this.on("playerConnect", this.onConnect.bind(this));
|
|
92
|
+
this.raknet.options.maxPacketsPerSecond = 20000;
|
|
93
|
+
}
|
|
94
|
+
onConnect(player) {
|
|
95
|
+
if (!(player.connection instanceof raknet_1.Connection))
|
|
96
|
+
return;
|
|
97
|
+
const bridgePlayer = new bridge_player_1.BridgePlayer(this, player);
|
|
98
|
+
this.clients.set(`${player.connection.getAddress().address}:${player.connection.getAddress().port}`, bridgePlayer);
|
|
99
|
+
this.emit("connect", bridgePlayer);
|
|
100
|
+
bridgePlayer.player.once("ClientCacheStatusPacket", (packet) => {
|
|
101
|
+
bridgePlayer.cacheStatus = packet.supported;
|
|
102
|
+
});
|
|
103
|
+
bridgePlayer.player.on("ClientToServerHandshakePacket", (packet) => {
|
|
104
|
+
console.log("ClientToServerHandshakePacket");
|
|
105
|
+
this.onLogin(bridgePlayer);
|
|
106
|
+
return;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
onLogin(player) {
|
|
110
|
+
const client = new client_1.Client({
|
|
111
|
+
host: this.options.destination.host,
|
|
112
|
+
port: this.options.destination.port,
|
|
113
|
+
version: "1.21.50",
|
|
114
|
+
offline: false,
|
|
115
|
+
tokensFolder: "tokens",
|
|
116
|
+
viewDistance: 2,
|
|
117
|
+
worker: true,
|
|
118
|
+
});
|
|
119
|
+
player.client = client;
|
|
120
|
+
client.cancelPastLogin = true;
|
|
121
|
+
client.removeAllListeners("ResourcePackStackPacket");
|
|
122
|
+
client.removeAllListeners("ResourcePacksInfoPacket");
|
|
123
|
+
client.removeAllListeners("PlayStatusPacket");
|
|
124
|
+
client.once("ResourcePacksInfoPacket", (packet) => {
|
|
125
|
+
client.send(client_cache_status_1.ClientCacheStatusPacket.create(false));
|
|
126
|
+
});
|
|
127
|
+
player.once("serverbound-ResourcePackClientResponsePacket", (packet) => {
|
|
128
|
+
player.player.send(client_cache_status_1.ClientCacheStatusPacket.create(false));
|
|
129
|
+
});
|
|
130
|
+
client.once("PlayStatusPacket", (packet) => {
|
|
131
|
+
if (packet.status !== protocol_1.PlayStatus.LoginSuccess)
|
|
132
|
+
throw new Error("Login failed");
|
|
133
|
+
client.processPacket = (buffer) => {
|
|
134
|
+
this.processPacketCommon(buffer, player, true, player.player);
|
|
135
|
+
};
|
|
136
|
+
player.player.processPacket = (buffer) => {
|
|
137
|
+
this.processPacketCommon(buffer, player, false, client);
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
client.connect();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.Bridge = Bridge;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { KeyObject } from "node:crypto";
|
|
2
|
+
import { LoginPacket } from "@serenityjs/protocol";
|
|
3
|
+
import type { Player } from "src/server/player";
|
|
4
|
+
import type { Client } from "./client";
|
|
5
|
+
import { type LoginData } from "./types/login-data";
|
|
6
|
+
import { type Payload } from "./types/payload";
|
|
7
|
+
declare class ClientData {
|
|
8
|
+
client: Client | Player;
|
|
9
|
+
/** This Contains a lot of Data for the Login Packet */
|
|
10
|
+
payload: Payload;
|
|
11
|
+
/** This Contains the Access Tokens from Auth */
|
|
12
|
+
accessToken: string[];
|
|
13
|
+
/** This Contains the Login Data */
|
|
14
|
+
loginData: LoginData;
|
|
15
|
+
/** This Contains the Shared Secret */
|
|
16
|
+
sharedSecret: Buffer;
|
|
17
|
+
constructor(client: Client | Player);
|
|
18
|
+
createLoginPacket(): LoginPacket;
|
|
19
|
+
createClientChain(mojangKey: string | null, offline: boolean): Promise<string>;
|
|
20
|
+
private createClientChainInternal;
|
|
21
|
+
createClientUserChain(privateKey: KeyObject): Promise<string>;
|
|
22
|
+
createSharedSecret(privateKey: KeyObject, publicKey: KeyObject): Buffer;
|
|
23
|
+
private validateKeys;
|
|
24
|
+
static nextUUID(): string;
|
|
25
|
+
static generateId(): number;
|
|
26
|
+
static OnlineId(): string;
|
|
27
|
+
}
|
|
28
|
+
export { ClientData };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientData = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const raknet_1 = require("@sanctumterra/raknet");
|
|
7
|
+
const protocol_1 = require("@serenityjs/protocol");
|
|
8
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
9
|
+
const uuid_1345_1 = require("uuid-1345");
|
|
10
|
+
const login_data_1 = require("./types/login-data");
|
|
11
|
+
const payload_1 = require("./types/payload");
|
|
12
|
+
const PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECRXueJeTDqNRRgJi/vlRufByu/2G0i2Ebt6YMar5QX/R0DIIyrJMcUpruK4QveTfJSTp3Shlq4Gk34cD/4GUWwkv0DVuzeuB+tXija7HBxii03NHDbPAD0AKnLr2wdAp";
|
|
13
|
+
const algorithm = "ES384";
|
|
14
|
+
const curve = "secp384r1";
|
|
15
|
+
const pem = { format: "pem", type: "sec1" };
|
|
16
|
+
const der = { format: "der", type: "spki" };
|
|
17
|
+
const signAsync = (0, node_util_1.promisify)(jsonwebtoken_1.sign);
|
|
18
|
+
class ClientData {
|
|
19
|
+
constructor(client) {
|
|
20
|
+
this.client = client;
|
|
21
|
+
this.payload = (0, payload_1.createDefaultPayload)(client);
|
|
22
|
+
this.loginData = (0, login_data_1.prepareLoginData)();
|
|
23
|
+
}
|
|
24
|
+
createLoginPacket() {
|
|
25
|
+
const loginPacket = new protocol_1.LoginPacket();
|
|
26
|
+
const chain = [this.loginData.clientIdentityChain, ...this.accessToken];
|
|
27
|
+
const userChain = this.loginData.clientUserChain;
|
|
28
|
+
const encodedChain = JSON.stringify({ chain });
|
|
29
|
+
loginPacket.protocol = this.client.protocol;
|
|
30
|
+
loginPacket.tokens = new protocol_1.LoginTokens(userChain, encodedChain);
|
|
31
|
+
return loginPacket;
|
|
32
|
+
}
|
|
33
|
+
async createClientChain(mojangKey, offline) {
|
|
34
|
+
return this.createClientChainInternal(mojangKey, offline);
|
|
35
|
+
}
|
|
36
|
+
async createClientChainInternal(mojangKey, offline) {
|
|
37
|
+
const { clientX509, ecdhKeyPair } = this.loginData;
|
|
38
|
+
let payload;
|
|
39
|
+
let signOptions;
|
|
40
|
+
if (offline) {
|
|
41
|
+
payload = {
|
|
42
|
+
extraData: {
|
|
43
|
+
displayName: this.client.profile.name,
|
|
44
|
+
identity: this.client.profile.uuid,
|
|
45
|
+
titleId: "89692877",
|
|
46
|
+
XUID: "0",
|
|
47
|
+
},
|
|
48
|
+
certificateAuthority: true,
|
|
49
|
+
identityPublicKey: clientX509,
|
|
50
|
+
};
|
|
51
|
+
signOptions = {
|
|
52
|
+
algorithm: algorithm,
|
|
53
|
+
notBefore: 0,
|
|
54
|
+
issuer: "self",
|
|
55
|
+
expiresIn: 60 * 60,
|
|
56
|
+
header: { alg: algorithm, x5u: clientX509, typ: undefined },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
payload = {
|
|
61
|
+
identityPublicKey: mojangKey || PUBLIC_KEY,
|
|
62
|
+
certificateAuthority: true,
|
|
63
|
+
};
|
|
64
|
+
signOptions = {
|
|
65
|
+
algorithm: algorithm,
|
|
66
|
+
header: { alg: algorithm, x5u: clientX509, typ: undefined },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return signAsync(payload, ecdhKeyPair.privateKey.export({ format: "pem", type: "pkcs8" }), signOptions);
|
|
70
|
+
}
|
|
71
|
+
async createClientUserChain(privateKey) {
|
|
72
|
+
const { clientX509 } = this.loginData;
|
|
73
|
+
const customPayload = this.client.options.skinData || {};
|
|
74
|
+
const payload = {
|
|
75
|
+
...this.payload,
|
|
76
|
+
...customPayload,
|
|
77
|
+
ServerAddress: `${this.client.options.host}:${this.client.options.port}`,
|
|
78
|
+
ClientRandomId: Date.now(),
|
|
79
|
+
DeviceId: ClientData.nextUUID(),
|
|
80
|
+
PlayFabId: ClientData.nextUUID().replace(/-/g, "").slice(0, 16),
|
|
81
|
+
SelfSignedId: ClientData.nextUUID(),
|
|
82
|
+
};
|
|
83
|
+
return signAsync(payload, privateKey.export({ format: "pem", type: "pkcs8" }), {
|
|
84
|
+
algorithm,
|
|
85
|
+
header: { alg: algorithm, x5u: clientX509, typ: undefined },
|
|
86
|
+
noTimestamp: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
createSharedSecret(privateKey, publicKey) {
|
|
90
|
+
this.validateKeys(privateKey, publicKey);
|
|
91
|
+
const curve = privateKey.asymmetricKeyDetails?.namedCurve;
|
|
92
|
+
if (!curve) {
|
|
93
|
+
throw new Error("Invalid private key format. Named curve is missing.");
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const ecdh = (0, node_crypto_1.createECDH)(curve);
|
|
97
|
+
const privateKeyJwk = privateKey.export({ format: "jwk" });
|
|
98
|
+
const publicKeyJwk = publicKey.export({ format: "jwk" });
|
|
99
|
+
if (!privateKeyJwk.d || !publicKeyJwk.x || !publicKeyJwk.y) {
|
|
100
|
+
throw new Error("Invalid key format. Missing 'd', 'x', or 'y' parameters.");
|
|
101
|
+
}
|
|
102
|
+
ecdh.setPrivateKey(new Uint8Array(Buffer.from(privateKeyJwk.d, "base64")));
|
|
103
|
+
const publicKeyBuffer = Buffer.concat([
|
|
104
|
+
new Uint8Array([0x04]),
|
|
105
|
+
new Uint8Array(Buffer.from(publicKeyJwk.x, "base64")),
|
|
106
|
+
new Uint8Array(Buffer.from(publicKeyJwk.y, "base64")),
|
|
107
|
+
]);
|
|
108
|
+
const computedSecret = ecdh.computeSecret(new Uint8Array(publicKeyBuffer));
|
|
109
|
+
return Buffer.from(new Uint8Array(computedSecret));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
raknet_1.Logger.error("Error computing shared secret:", error);
|
|
113
|
+
throw new Error("Failed to create shared secret.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
validateKeys(privateKey, publicKey) {
|
|
117
|
+
if (!(privateKey instanceof node_crypto_1.KeyObject) ||
|
|
118
|
+
!(publicKey instanceof node_crypto_1.KeyObject)) {
|
|
119
|
+
throw new Error("Both privateKey and publicKey must be crypto.KeyObject instances");
|
|
120
|
+
}
|
|
121
|
+
if (privateKey.type !== "private" || publicKey.type !== "public") {
|
|
122
|
+
throw new Error("Invalid key types. Expected private and public keys.");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
static nextUUID() {
|
|
126
|
+
return (0, uuid_1345_1.v3)({
|
|
127
|
+
namespace: "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
|
|
128
|
+
name: Date.now().toString(),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
static generateId() {
|
|
132
|
+
const randomNum = Math.floor(Math.random() * 9000000000000000000) + 1000000000000000000;
|
|
133
|
+
return -randomNum;
|
|
134
|
+
}
|
|
135
|
+
static OnlineId() {
|
|
136
|
+
const randomNum = Math.floor(Math.random() * 9000000000000000000) + 1000000000000000000;
|
|
137
|
+
return `${randomNum}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.ClientData = ClientData;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CompressionMethod, InputMode } from "@serenityjs/protocol";
|
|
2
|
+
import type * as Protocol from "@serenityjs/protocol";
|
|
3
|
+
import type { ClientCacheStatusPacket } from "../network/client-cache-status";
|
|
4
|
+
export declare enum ProtocolList {
|
|
5
|
+
"1.21.50" = 766
|
|
6
|
+
}
|
|
7
|
+
export declare enum DeviceOS {
|
|
8
|
+
Undefined = 0,
|
|
9
|
+
Android = 1,
|
|
10
|
+
IOS = 2,
|
|
11
|
+
OSX = 3,
|
|
12
|
+
FireOS = 4,
|
|
13
|
+
GearVR = 5,
|
|
14
|
+
Hololens = 6,
|
|
15
|
+
Win10 = 7,
|
|
16
|
+
Win32 = 8,
|
|
17
|
+
Dedicated = 9,
|
|
18
|
+
TVOS = 10,
|
|
19
|
+
Orbis = 11,
|
|
20
|
+
NintendoSwitch = 12,
|
|
21
|
+
Xbox = 13,
|
|
22
|
+
WindowsPhone = 14,
|
|
23
|
+
Linux = 15
|
|
24
|
+
}
|
|
25
|
+
type LoginPacketOptions = {
|
|
26
|
+
DeviceModel: string;
|
|
27
|
+
CurrentInputMode: InputMode;
|
|
28
|
+
DefaultInputMode: InputMode;
|
|
29
|
+
};
|
|
30
|
+
type ClientOptions = {
|
|
31
|
+
host: string;
|
|
32
|
+
port: number;
|
|
33
|
+
compressionThreshold: number;
|
|
34
|
+
compressionMethod: CompressionMethod;
|
|
35
|
+
compressionLevel: number;
|
|
36
|
+
deviceOS: DeviceOS;
|
|
37
|
+
version: keyof typeof ProtocolList;
|
|
38
|
+
username: string;
|
|
39
|
+
tokensFolder: string;
|
|
40
|
+
viewDistance: number;
|
|
41
|
+
skinData: object | undefined;
|
|
42
|
+
offline: boolean;
|
|
43
|
+
worker: boolean;
|
|
44
|
+
loginOptions: LoginPacketOptions;
|
|
45
|
+
};
|
|
46
|
+
declare const defaultClientOptions: ClientOptions;
|
|
47
|
+
type PacketNames = {
|
|
48
|
+
[K in keyof typeof Protocol]: K extends `${string}Packet` ? K extends "Packet" | "DataPacket" ? never : K : never;
|
|
49
|
+
}[keyof typeof Protocol];
|
|
50
|
+
type ClientEvents = {
|
|
51
|
+
[K in PacketNames]: [packet: InstanceType<(typeof Protocol)[K]>];
|
|
52
|
+
} & {
|
|
53
|
+
session: [];
|
|
54
|
+
ClientCacheStatus: [packet: ClientCacheStatusPacket];
|
|
55
|
+
} & {
|
|
56
|
+
packet: [packet: InstanceType<(typeof Protocol)[PacketNames]>];
|
|
57
|
+
};
|
|
58
|
+
export { type ClientOptions, defaultClientOptions, type ClientEvents, type PacketNames, };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultClientOptions = exports.DeviceOS = exports.ProtocolList = void 0;
|
|
4
|
+
const protocol_1 = require("@serenityjs/protocol");
|
|
5
|
+
var ProtocolList;
|
|
6
|
+
(function (ProtocolList) {
|
|
7
|
+
ProtocolList[ProtocolList["1.21.50"] = 766] = "1.21.50";
|
|
8
|
+
})(ProtocolList || (exports.ProtocolList = ProtocolList = {}));
|
|
9
|
+
var DeviceOS;
|
|
10
|
+
(function (DeviceOS) {
|
|
11
|
+
DeviceOS[DeviceOS["Undefined"] = 0] = "Undefined";
|
|
12
|
+
DeviceOS[DeviceOS["Android"] = 1] = "Android";
|
|
13
|
+
DeviceOS[DeviceOS["IOS"] = 2] = "IOS";
|
|
14
|
+
DeviceOS[DeviceOS["OSX"] = 3] = "OSX";
|
|
15
|
+
DeviceOS[DeviceOS["FireOS"] = 4] = "FireOS";
|
|
16
|
+
DeviceOS[DeviceOS["GearVR"] = 5] = "GearVR";
|
|
17
|
+
DeviceOS[DeviceOS["Hololens"] = 6] = "Hololens";
|
|
18
|
+
DeviceOS[DeviceOS["Win10"] = 7] = "Win10";
|
|
19
|
+
DeviceOS[DeviceOS["Win32"] = 8] = "Win32";
|
|
20
|
+
DeviceOS[DeviceOS["Dedicated"] = 9] = "Dedicated";
|
|
21
|
+
DeviceOS[DeviceOS["TVOS"] = 10] = "TVOS";
|
|
22
|
+
DeviceOS[DeviceOS["Orbis"] = 11] = "Orbis";
|
|
23
|
+
DeviceOS[DeviceOS["NintendoSwitch"] = 12] = "NintendoSwitch";
|
|
24
|
+
DeviceOS[DeviceOS["Xbox"] = 13] = "Xbox";
|
|
25
|
+
DeviceOS[DeviceOS["WindowsPhone"] = 14] = "WindowsPhone";
|
|
26
|
+
DeviceOS[DeviceOS["Linux"] = 15] = "Linux";
|
|
27
|
+
})(DeviceOS || (exports.DeviceOS = DeviceOS = {}));
|
|
28
|
+
const defaultClientOptions = {
|
|
29
|
+
host: "127.0.0.1",
|
|
30
|
+
port: 19132,
|
|
31
|
+
compressionThreshold: 1,
|
|
32
|
+
compressionMethod: protocol_1.CompressionMethod.Zlib,
|
|
33
|
+
compressionLevel: 7,
|
|
34
|
+
deviceOS: DeviceOS.NintendoSwitch,
|
|
35
|
+
version: "1.21.50",
|
|
36
|
+
username: "SanctumTerra",
|
|
37
|
+
tokensFolder: "tokens",
|
|
38
|
+
viewDistance: 10,
|
|
39
|
+
skinData: undefined,
|
|
40
|
+
offline: false,
|
|
41
|
+
worker: false,
|
|
42
|
+
loginOptions: {
|
|
43
|
+
DeviceModel: "SwimmingPool",
|
|
44
|
+
CurrentInputMode: protocol_1.InputMode.GamePad,
|
|
45
|
+
DefaultInputMode: protocol_1.InputMode.GamePad,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
exports.defaultClientOptions = defaultClientOptions;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Client as RaknetClient } from "@sanctumterra/raknet";
|
|
2
|
+
import { DataPacket } from "@serenityjs/protocol";
|
|
3
|
+
import { Emitter } from "../libs/emitter";
|
|
4
|
+
import { type Profile } from "../network";
|
|
5
|
+
import { PacketEncryptor } from "../network/packet-encryptor";
|
|
6
|
+
import { ClientData } from "./client-data";
|
|
7
|
+
import { type ClientEvents, type ClientOptions, ProtocolList } from "./client-options";
|
|
8
|
+
import { WorkerClient } from "./worker";
|
|
9
|
+
declare class Client extends Emitter<ClientEvents> {
|
|
10
|
+
raknet: RaknetClient | WorkerClient;
|
|
11
|
+
options: ClientOptions;
|
|
12
|
+
private startGamePacket?;
|
|
13
|
+
private status;
|
|
14
|
+
_encryptionEnabled: boolean;
|
|
15
|
+
_compressionEnabled: boolean;
|
|
16
|
+
private packetCompressor;
|
|
17
|
+
protocol: ProtocolList;
|
|
18
|
+
profile: Profile;
|
|
19
|
+
data: ClientData;
|
|
20
|
+
username: string;
|
|
21
|
+
sessionReady: boolean;
|
|
22
|
+
secretKeyBytes: Buffer;
|
|
23
|
+
iv: Buffer;
|
|
24
|
+
packetEncryptor: PacketEncryptor;
|
|
25
|
+
runtimeEntityId: bigint;
|
|
26
|
+
cancelPastLogin: boolean;
|
|
27
|
+
constructor(options: Partial<ClientOptions>);
|
|
28
|
+
connect(): Promise<unknown>;
|
|
29
|
+
private handleStartGamePacket;
|
|
30
|
+
private handleSetLocalPlayerAsInitializedPacket;
|
|
31
|
+
private handleEncapsulated;
|
|
32
|
+
private sendPacket;
|
|
33
|
+
send(packet: DataPacket | Buffer): void;
|
|
34
|
+
queue(packet: DataPacket | Buffer): void;
|
|
35
|
+
/** Already decompressed packets */
|
|
36
|
+
processPacket(buffer: Buffer): void;
|
|
37
|
+
private handleSession;
|
|
38
|
+
listen(): void;
|
|
39
|
+
private handlePlayStatusPacket;
|
|
40
|
+
private handleResourcePacksInfoPacket;
|
|
41
|
+
startEncryption(iv: Buffer): void;
|
|
42
|
+
sendMessage(text: string): void;
|
|
43
|
+
}
|
|
44
|
+
export { Client };
|