tangled
alpha
login
or
join now
dunkirk.sh
/
paper-crown
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
feat: update roster to be more resilient
dunkirk.sh
2 months ago
ccfd7c4a
427867f5
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+128
-78
1 changed file
expand all
collapse all
unified
split
proxy
roster-client.ts
+128
-78
proxy/roster-client.ts
···
1
1
import mc from "minecraft-protocol";
2
2
+
import { appendFileSync } from "fs";
2
3
3
4
type Player = {
4
5
uuid: string;
···
12
13
const TARGET_HOST = "localhost";
13
14
const TARGET_PORT = 25566;
14
15
const MINECRAFT_VERSION = "1.20.4";
16
16
+
const LOG_FILE = "roster.log";
17
17
+
const RECONNECT_DELAY = 5000;
15
18
16
19
const roster = new Map<string, Player>();
20
20
+
let client: mc.Client;
21
21
+
let reconnectTimeout: NodeJS.Timeout | null = null;
17
22
18
18
-
const client = mc.createClient({
19
19
-
host: TARGET_HOST,
20
20
-
port: TARGET_PORT,
21
21
-
username: "hepticWarbler",
22
22
-
version: MINECRAFT_VERSION,
23
23
-
keepAlive: true,
24
24
-
auth: "offline",
25
25
-
});
23
23
+
function log(message: string) {
24
24
+
const timestamp = new Date().toISOString();
25
25
+
const logEntry = `[${timestamp}] ${message}\n`;
26
26
+
appendFileSync(LOG_FILE, logEntry);
27
27
+
console.log(message);
28
28
+
}
26
29
27
27
-
client.on("login", () => {
28
28
-
console.log("ā Joined server, tracking player listā¦");
29
29
-
});
30
30
+
function setupClient(client: mc.Client) {
31
31
+
client.on("login", () => {
32
32
+
log("ā Joined server, tracking player listā¦");
33
33
+
});
30
34
31
31
-
client.on("packet", (data, meta) => {
32
32
-
if (meta.state !== "play") return;
35
35
+
client.on("packet", (data, meta) => {
36
36
+
if (meta.state !== "play") return;
33
37
34
34
-
switch (meta.name) {
35
35
-
case "player_info": {
36
36
-
const action = data.action;
37
37
-
for (const item of data.data) {
38
38
-
const uuid = item.UUID ?? item.uuid;
39
39
-
const name = item.name ?? item.username ?? "";
40
40
-
const entry = roster.get(uuid) ?? { uuid, name };
38
38
+
switch (meta.name) {
39
39
+
case "player_info": {
40
40
+
const action = data.action;
41
41
+
for (const item of data.data) {
42
42
+
const uuid = item.UUID ?? item.uuid;
43
43
+
const name = item.name ?? item.username ?? "";
44
44
+
const entry = roster.get(uuid) ?? { uuid, name };
41
45
42
42
-
if (action === 0) {
43
43
-
roster.set(uuid, { ...entry, name });
44
44
-
console.log(`ā join: ${name} (${uuid})`);
45
45
-
} else if (action === 1) {
46
46
-
roster.set(uuid, { ...entry, gameMode: item.gamemode });
47
47
-
} else if (action === 2) {
48
48
-
roster.set(uuid, { ...entry, latency: item.ping });
49
49
-
} else if (action === 4) {
50
50
-
roster.delete(uuid);
51
51
-
console.log(`ā leave: ${name} (${uuid})`);
46
46
+
if (action === 0) {
47
47
+
roster.set(uuid, { ...entry, name });
48
48
+
log(`ā join: ${name} (${uuid})`);
49
49
+
} else if (action === 1) {
50
50
+
roster.set(uuid, { ...entry, gameMode: item.gamemode });
51
51
+
} else if (action === 2) {
52
52
+
roster.set(uuid, { ...entry, latency: item.ping });
53
53
+
} else if (action === 4) {
54
54
+
roster.delete(uuid);
55
55
+
log(`ā leave: ${name} (${uuid})`);
56
56
+
}
52
57
}
58
58
+
break;
53
59
}
54
54
-
break;
55
55
-
}
56
56
-
case "player_info_update": {
57
57
-
const actions: string[] = data.actions;
58
58
-
for (const v of data.values) {
59
59
-
const uuid: string = v.uuid;
60
60
-
const prev = roster.get(uuid);
61
61
-
let name = prev?.name ?? v.name ?? v.profile?.name ?? "";
62
62
-
let listed = prev?.listed;
60
60
+
case "player_info_update": {
61
61
+
const actions: string[] = data.actions;
62
62
+
for (const v of data.values) {
63
63
+
const uuid: string = v.uuid;
64
64
+
const prev = roster.get(uuid);
65
65
+
let name = prev?.name ?? v.name ?? v.profile?.name ?? "";
66
66
+
let listed = prev?.listed;
63
67
64
64
-
if (actions.includes("add_player")) {
65
65
-
name = v.name ?? name;
66
66
-
listed = true;
67
67
-
roster.set(uuid, {
68
68
-
uuid,
69
69
-
name,
70
70
-
listed,
71
71
-
properties: v.properties,
72
72
-
latency: v.latency,
73
73
-
gameMode: v.gamemode,
74
74
-
});
75
75
-
console.log(`ā join: ${name} (${uuid})`);
76
76
-
}
77
77
-
if (actions.includes("update_latency")) {
78
78
-
roster.set(uuid, { ...(prev ?? { uuid, name }), latency: v.latency });
79
79
-
}
80
80
-
if (actions.includes("update_gamemode")) {
81
81
-
roster.set(uuid, {
82
82
-
...(prev ?? { uuid, name }),
83
83
-
gameMode: v.gamemode,
84
84
-
});
85
85
-
}
86
86
-
if (actions.includes("remove_player")) {
87
87
-
roster.delete(uuid);
88
88
-
console.log(`ā leave: ${name} (${uuid})`);
68
68
+
if (actions.includes("add_player")) {
69
69
+
name = v.name ?? name;
70
70
+
listed = true;
71
71
+
roster.set(uuid, {
72
72
+
uuid,
73
73
+
name,
74
74
+
listed,
75
75
+
properties: v.properties,
76
76
+
latency: v.latency,
77
77
+
gameMode: v.gamemode,
78
78
+
});
79
79
+
log(`ā join: ${name} (${uuid})`);
80
80
+
}
81
81
+
if (actions.includes("update_latency")) {
82
82
+
roster.set(uuid, { ...(prev ?? { uuid, name }), latency: v.latency });
83
83
+
}
84
84
+
if (actions.includes("update_gamemode")) {
85
85
+
roster.set(uuid, {
86
86
+
...(prev ?? { uuid, name }),
87
87
+
gameMode: v.gamemode,
88
88
+
});
89
89
+
}
90
90
+
if (actions.includes("remove_player")) {
91
91
+
roster.delete(uuid);
92
92
+
log(`ā leave: ${name} (${uuid})`);
93
93
+
}
89
94
}
95
95
+
break;
90
96
}
91
91
-
break;
92
97
}
93
93
-
}
94
94
-
});
98
98
+
});
99
99
+
100
100
+
client.on("error", (err) => {
101
101
+
if (err instanceof Error) {
102
102
+
log("ā client error: " + err.message);
103
103
+
if (err.stack) log(err.stack);
104
104
+
} else if (err && typeof err === "object" && "errors" in err) {
105
105
+
log("ā client error: AggregateError");
106
106
+
const errors = (err as any).errors;
107
107
+
if (Array.isArray(errors)) {
108
108
+
errors.forEach((e: any, i: number) => {
109
109
+
log(` [${i}] ${e.message || e}`);
110
110
+
});
111
111
+
}
112
112
+
} else {
113
113
+
log("ā client error: " + String(err));
114
114
+
}
115
115
+
});
116
116
+
117
117
+
client.on("end", (reason) => {
118
118
+
log("š Disconnected: " + reason);
119
119
+
scheduleReconnect();
120
120
+
});
121
121
+
122
122
+
client.on("kick_disconnect", (packet) => {
123
123
+
const reason = JSON.parse(packet.reason);
124
124
+
log("ā ļø Kicked from server: " + JSON.stringify(reason));
125
125
+
scheduleReconnect();
126
126
+
});
127
127
+
}
128
128
+
129
129
+
function scheduleReconnect() {
130
130
+
if (reconnectTimeout) return;
131
131
+
132
132
+
log(`š Reconnecting in ${RECONNECT_DELAY / 1000} seconds...`);
133
133
+
reconnectTimeout = setTimeout(() => {
134
134
+
reconnectTimeout = null;
135
135
+
connect();
136
136
+
}, RECONNECT_DELAY);
137
137
+
}
138
138
+
139
139
+
function connect() {
140
140
+
client = mc.createClient({
141
141
+
host: TARGET_HOST,
142
142
+
port: TARGET_PORT,
143
143
+
username: "hepticWarbler",
144
144
+
version: MINECRAFT_VERSION,
145
145
+
keepAlive: true,
146
146
+
auth: "offline",
147
147
+
});
148
148
+
149
149
+
setupClient(client);
150
150
+
}
151
151
+
152
152
+
connect();
95
153
96
154
setInterval(() => {
97
155
const players = Array.from(roster.values());
98
98
-
console.log("\nš Current players:", players.length);
156
156
+
log("\nš Current players: " + players.length);
99
157
players.forEach((p) => {
100
100
-
console.log(
158
158
+
log(
101
159
` - ${p.name} (${p.uuid}) | ping: ${p.latency}ms | mode: ${p.gameMode}`,
102
160
);
103
161
});
104
104
-
console.log("");
162
162
+
log("");
105
163
}, 10000);
106
106
-
107
107
-
client.on("error", (err) => {
108
108
-
console.error("ā client error:", err);
109
109
-
});
110
110
-
111
111
-
client.on("end", (reason) => {
112
112
-
console.log("š Disconnected:", reason);
113
113
-
});