@@ -12,9 +12,10 @@ module.exports = inject
1212const PI = Math . PI
1313const PI_2 = Math . PI * 2
1414const PHYSICS_INTERVAL_MS = 50
15- const PHYSICS_TIMESTEP = PHYSICS_INTERVAL_MS / 1000
15+ const PHYSICS_TIMESTEP = PHYSICS_INTERVAL_MS / 1000 // 0.05
1616
17- function inject ( bot , { physicsEnabled } ) {
17+ function inject ( bot , { physicsEnabled, maxCatchupTicks } ) {
18+ const PHYSICS_CATCHUP_TICKS = maxCatchupTicks ?? 4
1819 const world = { getBlock : ( pos ) => { return bot . blockAt ( pos , false ) } }
1920 const physics = Physics ( bot . registry , world )
2021
@@ -38,6 +39,7 @@ function inject (bot, { physicsEnabled }) {
3839 let lastPhysicsFrameTime = null
3940 let shouldUsePhysics = false
4041 bot . physicsEnabled = physicsEnabled ?? true
42+ let deadTicks = 21
4143
4244 const lastSent = {
4345 x : 0 ,
@@ -51,25 +53,44 @@ function inject (bot, { physicsEnabled }) {
5153
5254 // This function should be executed each tick (every 0.05 seconds)
5355 // How it works: https://gafferongames.com/post/fix_your_timestep/
56+
57+ // WARNING: THIS IS NOT ACCURATE ON WINDOWS (15.6 Timer Resolution)
58+ // use WSL or switch to Linux
59+ // see: https://discord.com/channels/413438066984747026/519952494768685086/901948718255833158
5460 let timeAccumulator = 0
61+ let catchupTicks = 0
5562 function doPhysics ( ) {
5663 const now = performance . now ( )
5764 const deltaSeconds = ( now - lastPhysicsFrameTime ) / 1000
5865 lastPhysicsFrameTime = now
5966
6067 timeAccumulator += deltaSeconds
61-
68+ catchupTicks = 0
6269 while ( timeAccumulator >= PHYSICS_TIMESTEP ) {
63- if ( bot . physicsEnabled && shouldUsePhysics ) {
64- physics . simulatePlayer ( new PlayerState ( bot , controlState ) , world ) . apply ( bot )
65- bot . emit ( 'physicsTick' )
66- bot . emit ( 'physicTick' ) // Deprecated, only exists to support old plugins. May be removed in the future
67- }
68- updatePosition ( PHYSICS_TIMESTEP )
70+ tickPhysics ( now )
6971 timeAccumulator -= PHYSICS_TIMESTEP
72+ catchupTicks ++
73+ if ( catchupTicks >= PHYSICS_CATCHUP_TICKS ) break
74+ }
75+ }
76+
77+ function tickPhysics ( now ) {
78+ if ( bot . blockAt ( bot . entity . position ) == null ) return // check if chunk is unloaded
79+ if ( bot . physicsEnabled && shouldUsePhysics ) {
80+ physics . simulatePlayer ( new PlayerState ( bot , controlState ) , world ) . apply ( bot )
81+ bot . emit ( 'physicsTick' )
82+ bot . emit ( 'physicTick' ) // Deprecated, only exists to support old plugins. May be removed in the future
83+ }
84+ if ( shouldUsePhysics ) {
85+ updatePosition ( now )
7086 }
7187 }
7288
89+ // remove this when 'physicTick' is removed
90+ bot . on ( 'newListener' , ( name ) => {
91+ if ( name === 'physicTick' ) console . warn ( 'Mineflayer detected that you are using a deprecated event (physicTick)! Please use this event (physicsTick) instead.' )
92+ } )
93+
7394 function cleanup ( ) {
7495 clearInterval ( doPhysicsTimer )
7596 doPhysicsTimer = null
@@ -117,17 +138,25 @@ function inject (bot, { physicsEnabled }) {
117138 return dYaw
118139 }
119140
120- function updatePosition ( dt ) {
121- // If you're dead, you're probably on the ground though ...
122- if ( ! bot . isAlive ) bot . entity . onGround = true
141+ // returns false if packet should be sent, true if not
142+ function sendPositionPacketInDeath ( ) {
143+ if ( bot . isAlive === true ) deadTicks = 0
144+ if ( bot . isAlive === false && deadTicks <= 20 ) deadTicks ++
145+ if ( deadTicks >= 20 ) return true
146+ return false
147+ }
148+
149+ function updatePosition ( now ) {
150+ // Only send updates for 20 ticks after death
151+ if ( sendPositionPacketInDeath ( ) ) return
123152
124153 // Increment the yaw in baby steps so that notchian clients (not the server) can keep up.
125154 const dYaw = deltaYaw ( bot . entity . yaw , lastSentYaw )
126155 const dPitch = bot . entity . pitch - ( lastSentPitch || 0 )
127156
128157 // Vanilla doesn't clamp yaw, so we don't want to do it either
129- const maxDeltaYaw = dt * physics . yawSpeed
130- const maxDeltaPitch = dt * physics . pitchSpeed
158+ const maxDeltaYaw = PHYSICS_TIMESTEP * physics . yawSpeed
159+ const maxDeltaPitch = PHYSICS_TIMESTEP * physics . pitchSpeed
131160 lastSentYaw += math . clamp ( - maxDeltaYaw , dYaw , maxDeltaYaw )
132161 lastSentPitch += math . clamp ( - maxDeltaPitch , dPitch , maxDeltaPitch )
133162
@@ -137,24 +166,28 @@ function inject (bot, { physicsEnabled }) {
137166 const onGround = bot . entity . onGround
138167
139168 // Only send a position update if necessary, select the appropriate packet
140- const positionUpdated = lastSent . x !== position . x || lastSent . y !== position . y || lastSent . z !== position . z
169+ const positionUpdated = lastSent . x !== position . x || lastSent . y !== position . y || lastSent . z !== position . z ||
170+ // Send a position update every second, even if no other update was made
171+ // This function rounds to the nearest 50ms (or PHYSICS_INTERVAL_MS) and checks if a second has passed.
172+ ( Math . round ( ( now - lastSent . time ) / PHYSICS_INTERVAL_MS ) * PHYSICS_INTERVAL_MS ) >= 1000
141173 const lookUpdated = lastSent . yaw !== yaw || lastSent . pitch !== pitch
142174
143- if ( positionUpdated && lookUpdated && bot . isAlive ) {
175+ if ( positionUpdated && lookUpdated ) {
144176 sendPacketPositionAndLook ( position , yaw , pitch , onGround )
145- } else if ( positionUpdated && bot . isAlive ) {
177+ lastSent . time = now // only reset if positionUpdated is true
178+ } else if ( positionUpdated ) {
146179 sendPacketPosition ( position , onGround )
147- } else if ( lookUpdated && bot . isAlive ) {
180+ lastSent . time = now // only reset if positionUpdated is true
181+ } else if ( lookUpdated ) {
148182 sendPacketLook ( yaw , pitch , onGround )
149- } else if ( performance . now ( ) - lastSent . time >= 1000 ) {
150- // Send a position packet every second, even if no update was made
151- sendPacketPosition ( position , onGround )
152- lastSent . time = performance . now ( )
153- } else if ( positionUpdateSentEveryTick && bot . isAlive ) {
183+ } else if ( positionUpdateSentEveryTick || onGround !== lastSent . onGround ) {
154184 // For versions < 1.12, one player packet should be sent every tick
155185 // for the server to update health correctly
186+ // For versions >= 1.12, onGround !== lastSent.onGround should be used, but it doesn't ever trigger outside of login
156187 bot . _client . write ( 'flying' , { onGround : bot . entity . onGround } )
157188 }
189+
190+ lastSent . onGround = bot . entity . onGround // onGround is always set
158191 }
159192
160193 bot . physics = physics
@@ -262,7 +295,14 @@ function inject (bot, { physicsEnabled }) {
262295 // player position and look (clientbound)
263296 bot . _client . on ( 'position' , ( packet ) => {
264297 bot . entity . height = 1.62
265- bot . entity . velocity . set ( 0 , 0 , 0 )
298+
299+ // Velocity is only set to 0 if the flag is not set, otherwise keep current velocity
300+ const vel = bot . entity . velocity
301+ vel . set (
302+ packet . flags & 1 ? vel . x : 0 ,
303+ packet . flags & 2 ? vel . y : 0 ,
304+ packet . flags & 4 ? vel . z : 0
305+ )
266306
267307 // If flag is set, then the corresponding value is relative, else it is absolute
268308 const pos = bot . entity . position
@@ -280,19 +320,14 @@ function inject (bot, { physicsEnabled }) {
280320
281321 if ( bot . supportFeature ( 'teleportUsesOwnPacket' ) ) {
282322 bot . _client . write ( 'teleport_confirm' , { teleportId : packet . teleportId } )
283- // Force send an extra packet to be like vanilla client
284- sendPacketPositionAndLook ( pos , newYaw , newPitch , bot . entity . onGround )
285323 }
286324 sendPacketPositionAndLook ( pos , newYaw , newPitch , bot . entity . onGround )
287325
288326 shouldUsePhysics = true
289- bot . entity . timeSinceOnGround = 0
327+ bot . jumpTicks = 0
290328 lastSentYaw = bot . entity . yaw
329+ lastSentPitch = bot . entity . pitch
291330
292- if ( doPhysicsTimer === null ) {
293- lastPhysicsFrameTime = performance . now ( )
294- doPhysicsTimer = setInterval ( doPhysics , PHYSICS_INTERVAL_MS )
295- }
296331 bot . emit ( 'forcedMove' )
297332 } )
298333
@@ -313,5 +348,12 @@ function inject (bot, { physicsEnabled }) {
313348
314349 bot . on ( 'mount' , ( ) => { shouldUsePhysics = false } )
315350 bot . on ( 'respawn' , ( ) => { shouldUsePhysics = false } )
351+ bot . on ( 'login' , ( ) => {
352+ shouldUsePhysics = false
353+ if ( doPhysicsTimer === null ) {
354+ lastPhysicsFrameTime = performance . now ( )
355+ doPhysicsTimer = setInterval ( doPhysics , PHYSICS_INTERVAL_MS )
356+ }
357+ } )
316358 bot . on ( 'end' , cleanup )
317359}
0 commit comments