Skip to content

Commit b9d4e30

Browse files
StarWeizzSimeon Kummer
authored andcommitted
Add Velocity proxy support for Minecraft 1.20.2+ configuration phase
Fixes #3764 This commit adds proper handling of the configuration phase introduced in Minecraft 1.20.2 and extended in 1.21+, which is required for server transfers via Velocity proxy. Changes: - New velocity plugin to manage configuration phase state - Modified physics plugin to block movement packets during configuration - Automatic resource pack acceptance during configuration phase - New examples demonstrating Velocity proxy connection and transfers The bot will no longer be kicked with 'Disconnected, while in configuration phase' errors during Velocity server transfers.
1 parent 0d7cac0 commit b9d4e30

5 files changed

Lines changed: 318 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* This example shows how to disable the velocity plugin
3+
* and manually handle resource packs during Velocity transfers
4+
*
5+
* Use this if you want full control over resource pack acceptance
6+
* instead of the automatic behavior.
7+
*
8+
* WARNING: Disabling the velocity plugin will cause bot disconnections
9+
* during Velocity server transfers unless you handle the configuration
10+
* phase manually (advanced users only).
11+
*/
12+
const mineflayer = require('mineflayer')
13+
14+
if (process.argv.length < 4 || process.argv.length > 6) {
15+
console.log('Usage : node velocity_manual_resourcepack.js <host> <port> [<name>] [online?]')
16+
process.exit(1)
17+
}
18+
19+
const bot = mineflayer.createBot({
20+
host: process.argv[2],
21+
port: parseInt(process.argv[3]),
22+
username: process.argv[4] ? process.argv[4] : 'manual_bot',
23+
auth: process.argv[5] ? 'microsoft' : 'offline',
24+
// Disable the velocity plugin
25+
plugins: {
26+
velocity: false
27+
}
28+
})
29+
30+
bot.on('login', () => {
31+
console.log('Logged in (velocity plugin disabled)')
32+
})
33+
34+
bot.on('spawn', () => {
35+
console.log('Spawned in server:', bot.game.dimension)
36+
})
37+
38+
// Manually accept resource packs
39+
bot.on('resourcePack', (url, uuid) => {
40+
console.log('Resource pack received:', url)
41+
console.log('Accepting manually...')
42+
bot.acceptResourcePack()
43+
})
44+
45+
bot.on('end', (reason) => {
46+
console.log('Bot disconnected:', reason)
47+
})
48+
49+
bot.on('error', (err) => {
50+
console.error('Error:', err.message)
51+
})
52+
53+
bot.on('kicked', (reason) => {
54+
console.log('Kicked:', reason)
55+
console.log('Note: If kicked during transfer, you may need to enable the velocity plugin')
56+
})

examples/velocity_transfer.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* This example demonstrates connecting to a Velocity proxy server
3+
* and handling server transfers (1.20.2+)
4+
*
5+
* The velocity plugin is loaded by default and will automatically:
6+
* - Handle the configuration phase during transfers
7+
* - Accept resource packs during configuration
8+
* - Block physics packets while in configuration phase
9+
* - Re-enable physics after configuration completes
10+
*
11+
* To disable the velocity plugin, add to createBot options:
12+
* plugins: { velocity: false }
13+
*
14+
* Note: The plugin is harmless even if loaded - it only activates
15+
* during configuration phase (Velocity transfers, not normal gameplay)
16+
*
17+
* This fixes the issue: https://github.com/PrismarineJS/mineflayer/issues/3764
18+
*/
19+
const mineflayer = require('mineflayer')
20+
21+
if (process.argv.length < 4 || process.argv.length > 6) {
22+
console.log('Usage : node velocity_transfer.js <host> <port> [<name>] [online?]')
23+
process.exit(1)
24+
}
25+
26+
const bot = mineflayer.createBot({
27+
host: process.argv[2],
28+
port: parseInt(process.argv[3]),
29+
username: process.argv[4] ? process.argv[4] : 'velocity_bot',
30+
auth: process.argv[5] ? 'microsoft' : 'offline'
31+
// The velocity plugin is enabled by default
32+
// To disable it, uncomment the following line:
33+
// plugins: { velocity: false }
34+
})
35+
36+
bot.on('login', () => {
37+
console.log('Logged in to Velocity proxy')
38+
})
39+
40+
bot.on('spawn', () => {
41+
console.log('Spawned in server:', bot.game.dimension)
42+
})
43+
44+
// Track configuration phase events
45+
bot.on('configurationPhase', (phase) => {
46+
if (phase === 'start') {
47+
console.log('Entering configuration phase (server transfer starting...)')
48+
} else if (phase === 'end') {
49+
console.log('Exiting configuration phase (server transfer complete)')
50+
}
51+
})
52+
53+
// Optional: Track resource pack acceptance
54+
bot.on('resourcePack', (url, uuid) => {
55+
console.log('Resource pack received:', url)
56+
console.log('Note: Resource packs are automatically accepted during configuration phase')
57+
})
58+
59+
bot.on('end', (reason) => {
60+
console.log('Bot disconnected:', reason)
61+
})
62+
63+
bot.on('error', (err) => {
64+
console.error('Error:', err.message)
65+
})
66+
67+
bot.on('kicked', (reason) => {
68+
console.log('Kicked:', reason)
69+
})

lib/loader.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const plugins = {
2525
health: require('./plugins/health'),
2626
inventory: require('./plugins/inventory'),
2727
kick: require('./plugins/kick'),
28+
velocity: require('./plugins/velocity'),
2829
physics: require('./plugins/physics'),
2930
place_block: require('./plugins/place_block'),
3031
rain: require('./plugins/rain'),

lib/plugins/physics.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
9898
}
9999

100100
function sendPacketPosition (position, onGround) {
101+
// Don't send position packets during configuration phase (Velocity support)
102+
if (bot.inConfigurationPhase) return
103+
101104
// sends data, no logic
102105
const oldPos = new Vec3(lastSent.x, lastSent.y, lastSent.z)
103106
lastSent.x = position.x
@@ -110,6 +113,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
110113
}
111114

112115
function sendPacketLook (yaw, pitch, onGround) {
116+
// Don't send look packets during configuration phase (Velocity support)
117+
if (bot.inConfigurationPhase) return
118+
113119
// sends data, no logic
114120
const oldPos = new Vec3(lastSent.x, lastSent.y, lastSent.z)
115121
lastSent.yaw = yaw
@@ -121,6 +127,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
121127
}
122128

123129
function sendPacketPositionAndLook (position, yaw, pitch, onGround) {
130+
// Don't send position_look packets during configuration phase (Velocity support)
131+
if (bot.inConfigurationPhase) return
132+
124133
// sends data, no logic
125134
const oldPos = new Vec3(lastSent.x, lastSent.y, lastSent.z)
126135
lastSent.x = position.x
@@ -154,6 +163,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
154163
// Only send updates for 20 ticks after death
155164
if (isEntityRemoved()) return
156165

166+
// Don't send any position packets during configuration phase (Velocity support)
167+
if (bot.inConfigurationPhase) return
168+
157169
// Increment the yaw in baby steps so that notchian clients (not the server) can keep up.
158170
const dYaw = deltaYaw(bot.entity.yaw, lastSentYaw)
159171
const dPitch = bot.entity.pitch - (lastSentPitch || 0)

lib/plugins/velocity.js

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Velocity proxy support for Minecraft 1.20.2+ configuration phase
3+
*
4+
* This plugin manages the configuration phase introduced in Minecraft 1.20.2
5+
* and extended in 1.21+, which is required for proper server transfers via
6+
* Velocity proxy.
7+
*
8+
* During the configuration phase:
9+
* - Gameplay packets (movement, physics) are not allowed
10+
* - Only configuration packets should be sent
11+
* - Resource packs must be handled correctly
12+
*
13+
* Issue: https://github.com/PrismarineJS/mineflayer/issues/3764
14+
*/
15+
16+
module.exports = inject
17+
18+
function inject (bot) {
19+
// Track whether we're in configuration phase
20+
bot.inConfigurationPhase = false
21+
let configurationFinished = false
22+
23+
// Resource pack response codes
24+
const TEXTURE_PACK_RESULTS = {
25+
SUCCESSFULLY_LOADED: 0,
26+
DECLINED: 1,
27+
FAILED_DOWNLOAD: 2,
28+
ACCEPTED: 3
29+
}
30+
31+
/**
32+
* Enter configuration phase
33+
*/
34+
function enterConfigurationPhase () {
35+
if (bot.inConfigurationPhase) return
36+
37+
bot.inConfigurationPhase = true
38+
configurationFinished = false
39+
40+
// Disable physics during configuration to prevent movement packets
41+
if (bot.physicsEnabled !== undefined) {
42+
bot._physicsWasEnabled = bot.physicsEnabled
43+
bot.physicsEnabled = false
44+
}
45+
46+
bot.emit('configurationPhase', 'start')
47+
}
48+
49+
/**
50+
* Exit configuration phase
51+
*/
52+
function exitConfigurationPhase () {
53+
if (!bot.inConfigurationPhase) return
54+
55+
bot.inConfigurationPhase = false
56+
57+
// Re-enable physics after configuration
58+
setTimeout(() => {
59+
if (bot._physicsWasEnabled && !bot._ended) {
60+
bot.physicsEnabled = true
61+
delete bot._physicsWasEnabled
62+
}
63+
}, 2000)
64+
65+
bot.emit('configurationPhase', 'end')
66+
}
67+
68+
// === CONFIGURATION PHASE DETECTION ===
69+
70+
// Method 1: start_configuration event (1.20.2+)
71+
bot._client.on('start_configuration', () => {
72+
enterConfigurationPhase()
73+
})
74+
75+
// Method 2: select_known_packs event (1.21+)
76+
bot._client.on('select_known_packs', (packet) => {
77+
enterConfigurationPhase()
78+
// Note: No response needed for select_known_packs
79+
})
80+
81+
// Method 3: registry_data event (1.20.2+)
82+
bot._client.on('registry_data', (packet) => {
83+
// Only enter if not already in configuration phase
84+
// (registry_data can be sent multiple times)
85+
if (!bot.inConfigurationPhase) {
86+
enterConfigurationPhase()
87+
}
88+
})
89+
90+
// Method 4: transfer event (1.20.5+)
91+
bot._client.on('transfer', (packet) => {
92+
enterConfigurationPhase()
93+
})
94+
95+
// === AUTOMATIC RESOURCE PACK ACCEPTANCE ===
96+
97+
/**
98+
* Automatically accept resource packs during configuration phase
99+
* This is required for Velocity transfers to complete successfully
100+
*/
101+
102+
// Handle add_resource_pack (1.20.3+)
103+
bot._client.prependListener('add_resource_pack', (data) => {
104+
if (!bot.inConfigurationPhase) return
105+
106+
// Send ACCEPTED immediately
107+
bot._client.write('resource_pack_receive', {
108+
uuid: data.uuid,
109+
result: TEXTURE_PACK_RESULTS.ACCEPTED
110+
})
111+
112+
// Send SUCCESSFULLY_LOADED immediately
113+
// Server needs this before sending finish_configuration
114+
bot._client.write('resource_pack_receive', {
115+
uuid: data.uuid,
116+
result: TEXTURE_PACK_RESULTS.SUCCESSFULLY_LOADED
117+
})
118+
})
119+
120+
// Handle resource_pack_send (older versions with UUID support)
121+
bot._client.prependListener('resource_pack_send', (data) => {
122+
if (!bot.inConfigurationPhase) return
123+
if (!data.uuid) return // Only handle UUID-based resource packs
124+
125+
const UUID = require('uuid-1345')
126+
const uuid = new UUID(data.uuid)
127+
128+
// Send ACCEPTED immediately
129+
bot._client.write('resource_pack_receive', {
130+
uuid: uuid,
131+
result: TEXTURE_PACK_RESULTS.ACCEPTED
132+
})
133+
134+
// Send SUCCESSFULLY_LOADED immediately
135+
bot._client.write('resource_pack_receive', {
136+
uuid: uuid,
137+
result: TEXTURE_PACK_RESULTS.SUCCESSFULLY_LOADED
138+
})
139+
})
140+
141+
// === FINISH CONFIGURATION ===
142+
143+
bot._client.on('finish_configuration', () => {
144+
configurationFinished = true
145+
146+
// Acknowledge finish_configuration
147+
try {
148+
bot._client.write('finish_configuration', {})
149+
} catch (err) {
150+
// Ignore if packet doesn't exist for this version
151+
}
152+
153+
// Exit configuration phase after a short delay
154+
setTimeout(() => {
155+
exitConfigurationPhase()
156+
}, 500)
157+
})
158+
159+
// === FALLBACKS ===
160+
161+
// Fallback: Exit on login if we're still in configuration phase
162+
bot.on('login', () => {
163+
if (bot.inConfigurationPhase && configurationFinished) {
164+
exitConfigurationPhase()
165+
}
166+
})
167+
168+
// Fallback: Exit on spawn if we're still in configuration phase
169+
bot.on('spawn', () => {
170+
if (bot.inConfigurationPhase) {
171+
exitConfigurationPhase()
172+
}
173+
})
174+
175+
// Cleanup on end
176+
bot.on('end', () => {
177+
bot.inConfigurationPhase = false
178+
configurationFinished = false
179+
})
180+
}

0 commit comments

Comments
 (0)