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