Skip to content

Commit 4bd2aef

Browse files
committed
Fix player chat message parsing on 1.19.3 - 1.21.4
Minecraft 1.19.1/1.19.2 chat message parsing is busted and unlikely to be fixed due to a lack of manpower. PRs are welcome.
1 parent f99a383 commit 4bd2aef

File tree

3 files changed

+82
-28
lines changed

3 files changed

+82
-28
lines changed

src/minecraft/packets/chat.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,84 @@
1+
import type { MinecraftChat } from '../chatToJsx'
12
import { concatPacketData, type PacketDataTypes } from '../packet'
2-
import { protocolMap, writeVarInt } from '../utils'
3+
import { parseChat, protocolMap, readVarInt, writeVarInt } from '../utils'
34
import packetIds from './ids'
45

6+
interface PlayerChatMessage {
7+
signedChat: MinecraftChat
8+
unsignedChat?: MinecraftChat
9+
type: number
10+
displayName: MinecraftChat
11+
}
12+
13+
export const parsePlayerChatMessage = (data: Buffer, version: number): PlayerChatMessage => {
14+
return version >= protocolMap['1.19.3']
15+
? parsePlayerChatMessage1193(data, version)
16+
: version >= protocolMap['1.19.1']
17+
? parsePlayerChatMessage1191(data, version)
18+
: parsePlayerChatMessage119(data, version)
19+
}
20+
21+
const parsePlayerChatMessage119 = (data: Buffer, version: number): PlayerChatMessage => {
22+
const [signedChat, signedChatLength] = parseChat(data, version)
23+
data = data.slice(signedChatLength)
24+
const hasUnsignedChat = data.readInt8()
25+
data = data.slice(1)
26+
let unsignedChat: MinecraftChat | undefined
27+
if (hasUnsignedChat) {
28+
let unsignedChatLength
29+
;[unsignedChat, unsignedChatLength] = parseChat(data, version)
30+
data = data.slice(unsignedChatLength)
31+
}
32+
const [type, typeLength] = readVarInt(data)
33+
data = data.slice(typeLength)
34+
data = data.slice(16) // Skip sender UUID
35+
const [displayName, displayNameLength] = parseChat(data, version)
36+
data = data.slice(displayNameLength)
37+
return { signedChat, unsignedChat, type, displayName }
38+
}
39+
40+
const parsePlayerChatMessage1191 = (data: Buffer, version: number): PlayerChatMessage => {
41+
// TODO-1.19: https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol?oldid=2772944
42+
// This is completely busted for now. Is it worth implementing? Who is using 1.19.1/1.19.2 anyway?
43+
return parsePlayerChatMessage119(data, version)
44+
}
45+
46+
const parsePlayerChatMessage1193 = (data: Buffer, version: number): PlayerChatMessage => {
47+
data = data.slice(16) // Skip sender UUID
48+
data = data.slice(readVarInt(data)[1]) // Skip index
49+
const hasSignature = data.readInt8()
50+
data = data.slice(1) // Has signature
51+
if (hasSignature) data = data.slice(256) // Skip signature
52+
const [signedChat, signedChatLength] = parseChat(data)
53+
data = data.slice(signedChatLength + 8 + 8) // Skip timestamp and salt
54+
const [signatures, signaturesLength] = readVarInt(data)
55+
data = data.slice(signaturesLength)
56+
for (let i = 0; i < signatures; i++) {
57+
const [, idLength] = readVarInt(data)
58+
data = data.slice(idLength + 256) // Skip message ID and signature
59+
}
60+
const hasUnsignedChat = data.readInt8()
61+
data = data.slice(1)
62+
let unsignedChat: MinecraftChat | undefined
63+
if (hasUnsignedChat) {
64+
let unsignedChatLength
65+
;[unsignedChat, unsignedChatLength] = parseChat(data, version)
66+
data = data.slice(unsignedChatLength)
67+
}
68+
const [filterType, filterTypeLength] = readVarInt(data)
69+
data = data.slice(filterTypeLength)
70+
if (filterType === 2) {
71+
const [filterTypeBits, filterTypeBitsLength] = readVarInt(data)
72+
data = data.slice(filterTypeBitsLength + filterTypeBits * 8)
73+
}
74+
const [type, typeLength] = readVarInt(data)
75+
data = data.slice(typeLength)
76+
const [displayName, displayNameLength] = parseChat(data, version)
77+
data = data.slice(displayNameLength)
78+
// Skip target name
79+
return { signedChat, unsignedChat, type, displayName }
80+
}
81+
582
export const makeChatMessagePacket = (
683
msg: string,
784
protocolVersion: number,

src/screens/ChatScreen.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ const ChatScreen = ({ navigation, route }: Props): React.JSX.Element => {
161161
// Packet handler useEffect.
162162
useEffect(() => {
163163
if (!connection) return
164+
// TODO: This useEffect takes some time to kick in, during which Minecraft may send packets
165+
// Typically happens with locally running Velocity proxies which are super fast and zero latency
166+
// Extremely annoying though, it causes the ChatScreen to be stuck on "Logging in..."
164167
connection.on(
165168
'packet',
166169
packetHandler(

src/utilities/connection/packetHandler.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { MinecraftChat } from '../../minecraft/chatToJsx'
33
import { ConnectionState, type ServerConnection } from '../../minecraft/connection'
44
import { concatPacketData, type Packet, type PacketDataTypes } from '../../minecraft/packet'
55
import { parseChat, protocolMap, readVarInt, writeVarInt } from '../../minecraft/utils'
6-
import { makeChatMessagePacket } from '../../minecraft/packets/chat'
6+
import { makeChatMessagePacket, parsePlayerChatMessage } from '../../minecraft/packets/chat'
77
import packetIds from '../../minecraft/packets/ids'
88

99
export const enderChatPrefix = '\u00A74[\u00A7cEnderChat\u00A74] \u00A7c'
@@ -22,32 +22,6 @@ type HandleError = (
2222
translated: string,
2323
) => (error: unknown) => void
2424

25-
interface PlayerChatMessage {
26-
signedChat: MinecraftChat
27-
unsignedChat?: MinecraftChat
28-
type: number
29-
displayName: MinecraftChat
30-
}
31-
32-
const parsePlayerChatMessage = (data: Buffer, version: number): PlayerChatMessage => {
33-
const [signedChat, signedChatLength] = parseChat(data, version)
34-
data = data.slice(signedChatLength)
35-
const hasUnsignedChat = data.readInt8()
36-
data = data.slice(1)
37-
let unsignedChat: MinecraftChat | undefined
38-
if (hasUnsignedChat && version < protocolMap['1.20.3']) {
39-
let unsignedChatLength
40-
;[unsignedChat, unsignedChatLength] = parseChat(data, version)
41-
data = data.slice(unsignedChatLength)
42-
}
43-
const [type, typeLength] = readVarInt(data)
44-
data = data.slice(typeLength)
45-
data = data.slice(16) // Skip sender UUID
46-
const [displayName, displayNameLength] = parseChat(data, version)
47-
data = data.slice(displayNameLength)
48-
return { signedChat, unsignedChat, type, displayName }
49-
}
50-
5125
const handleSystemMessage = (
5226
packet: Packet,
5327
addMessage: (text: MinecraftChat) => void,

0 commit comments

Comments
 (0)