@@ -21,10 +21,11 @@ export default function GameCanvas() {
2121 const cameraPitchRef = useRef ( 0.5 ) // Vertical angle (pitch) - 0 to 1, 0.5 is middle
2222 const cameraDistanceRef = useRef ( 20 ) // For zoom
2323 const targetedEnemiesRef = useRef ( new Set ( ) )
24- const playerTargetPositionsRef = useRef ( { } ) // Track target positions for interpolation
25- const localPlayerPositionRef = useRef ( { x : 0 , y : 0.5 , z : 0 } ) // Local player ACTUAL position (no interpolation)
26- const pendingMovementRef = useRef ( { x : 0 , z : 0 } ) // Movement to apply this frame
2724 const socketRef = useRef ( null ) // Socket ref for animation loop access
25+ const sceneTransitionRef = useRef ( false ) // True when switching scenes - snap positions instead of interpolating
26+
27+ // SIMPLE APPROACH: All positions come from the store. Period.
28+ // The store is the single source of truth for ALL player positions.
2829
2930 const { socket, playerId, players, enemies, inHubWorld, inDungeon, weaponStats, targetedEnemies, damageNumbers, removeDamageNumber, panelCollapsed, chatBubbles } = useGameStore ( )
3031
@@ -94,8 +95,9 @@ export default function GameCanvas() {
9495
9596 const currentPlayerId = useGameStore . getState ( ) . playerId
9697 const currentSocket = socketRef . current
98+ const currentPlayers = useGameStore . getState ( ) . players
9799
98- // Process local player movement FIRST (client-side prediction)
100+ // Process keyboard input and send to server
99101 const keys = keysRef . current
100102 let dx = 0 , dz = 0
101103 const moveSpeed = 0.15
@@ -116,35 +118,22 @@ export default function GameCanvas() {
116118 const moveX = dx * Math . cos ( angle ) + dz * Math . sin ( angle )
117119 const moveZ = - dx * Math . sin ( angle ) + dz * Math . cos ( angle )
118120
119- // Update local position IMMEDIATELY (no interpolation for self)
120- localPlayerPositionRef . current . x += moveX * moveSpeed
121- localPlayerPositionRef . current . z += moveZ * moveSpeed
122-
123- // Send to server
121+ // Just send to server - server will update position and broadcast back
124122 currentSocket . emit ( 'updatePlayerPosition' , {
125123 x : moveX * moveSpeed ,
126124 z : moveZ * moveSpeed
127125 } )
128126 }
129127
130- // Update local player mesh DIRECTLY (no interpolation = no jitter)
131- if ( currentPlayerId && playersRef . current [ currentPlayerId ] ) {
132- const localMesh = playersRef . current [ currentPlayerId ]
133- localMesh . position . x = localPlayerPositionRef . current . x
134- localMesh . position . y = localPlayerPositionRef . current . y
135- localMesh . position . z = localPlayerPositionRef . current . z
136- }
137-
138- // Interpolate OTHER players (not self) toward their server positions
139- const interpolationSpeed = 0.2
128+ // Update ALL player meshes from store positions (simple interpolation for smoothness)
129+ const interpolationSpeed = 0.3
140130 Object . keys ( playersRef . current ) . forEach ( id => {
141- if ( id === currentPlayerId ) return // Skip local player
142131 const mesh = playersRef . current [ id ]
143- const targetPos = playerTargetPositionsRef . current [ id ]
144- if ( mesh && targetPos ) {
145- mesh . position . x += ( targetPos . x - mesh . position . x ) * interpolationSpeed
146- mesh . position . y += ( targetPos . y - mesh . position . y ) * interpolationSpeed
147- mesh . position . z += ( targetPos . z - mesh . position . z ) * interpolationSpeed
132+ const playerData = currentPlayers [ id ]
133+ if ( mesh && playerData ?. position ) {
134+ mesh . position . x += ( playerData . position . x - mesh . position . x ) * interpolationSpeed
135+ mesh . position . y += ( playerData . position . y - mesh . position . y ) * interpolationSpeed
136+ mesh . position . z += ( playerData . position . z - mesh . position . z ) * interpolationSpeed
148137 }
149138 } )
150139
@@ -232,6 +221,10 @@ export default function GameCanvas() {
232221 }
233222
234223 loadSceneAsync ( )
224+
225+ // Mark that we're in a scene transition - positions should snap, not interpolate
226+ sceneTransitionRef . current = true
227+ console . log ( '[GameCanvas] Scene transition started - will snap positions' )
235228 } , [ inHubWorld , inDungeon ] )
236229
237230 // Handle pointer lock for camera control
@@ -342,7 +335,10 @@ export default function GameCanvas() {
342335 existingMesh . geometry . dispose ( )
343336 existingMesh . material . dispose ( )
344337 delete playersRef . current [ id ]
345- delete playerTargetPositionsRef . current [ id ]
338+ } else if ( player . position && sceneTransitionRef . current ) {
339+ // During scene transitions, snap all positions immediately
340+ existingMesh . position . set ( player . position . x , player . position . y , player . position . z )
341+ console . log ( `[GameCanvas] Snapped player ${ id } to position during scene transition` )
346342 }
347343 }
348344
@@ -357,72 +353,31 @@ export default function GameCanvas() {
357353 const mesh = new THREE . Mesh ( geometry , material )
358354 mesh . castShadow = true
359355 mesh . userData . shape = shape
360- mesh . userData . isPlayer = true // ← IMPORTANT: Mark as player so it's not cleared by SceneLoader!
361- const initialPos = {
362- x : player . position ?. x || 0 ,
363- y : player . position ?. y || 0.5 ,
364- z : player . position ?. z || 0
365- }
366- mesh . position . set ( initialPos . x , initialPos . y , initialPos . z )
356+ mesh . userData . isPlayer = true
367357
368- // For local player, initialize our authoritative position ref
369- if ( id === currentPlayerId ) {
370- localPlayerPositionRef . current = { ...initialPos }
371- }
372- // For other players, initialize target for interpolation
373- playerTargetPositionsRef . current [ id ] = { ...initialPos }
358+ // Set initial position from server data
359+ const pos = player . position || { x : 0 , y : 0.5 , z : 0 }
360+ mesh . position . set ( pos . x , pos . y , pos . z )
374361
375362 scene . add ( mesh )
376363 playersRef . current [ id ] = mesh
377364 console . log ( `Created player mesh for ${ id } :` , { shape, color : color . toString ( 16 ) , position : player . position } )
378- } else {
379- // Update positions based on server data
380- if ( player . position ) {
381- if ( id === currentPlayerId ) {
382- // For local player: smoothly reconcile with server to prevent drift
383- // Apply gradual correction toward server position
384- const localPos = localPlayerPositionRef . current
385- const serverPos = player . position
386- const dx = serverPos . x - localPos . x
387- const dz = serverPos . z - localPos . z
388- const drift = Math . sqrt ( dx * dx + dz * dz )
389-
390- if ( drift > 0.5 ) {
391- // Large drift - snap immediately (teleport/desync)
392- if ( drift > 2 ) {
393- localPlayerPositionRef . current = {
394- x : serverPos . x ,
395- y : serverPos . y ,
396- z : serverPos . z
397- }
398- } else {
399- // Medium drift - blend toward server position to correct over time
400- const correction = 0.1 // 10% correction per update
401- localPlayerPositionRef . current . x += dx * correction
402- localPlayerPositionRef . current . z += dz * correction
403- }
404- }
405- // Small drift (<0.5) - ignore, client prediction is close enough
406- } else {
407- // For other players, update their interpolation target
408- playerTargetPositionsRef . current [ id ] = {
409- x : player . position . x ,
410- y : player . position . y ,
411- z : player . position . z
412- }
413- }
414- }
415365 }
416366 } )
417367
368+ // Clear scene transition flag after processing all players
369+ if ( sceneTransitionRef . current ) {
370+ sceneTransitionRef . current = false
371+ console . log ( '[GameCanvas] Scene transition complete - resuming interpolation' )
372+ }
373+
418374 // Remove disconnected players
419375 Object . keys ( playersRef . current ) . forEach ( id => {
420376 if ( ! players [ id ] ) {
421377 scene . remove ( playersRef . current [ id ] )
422378 playersRef . current [ id ] . geometry . dispose ( )
423379 playersRef . current [ id ] . material . dispose ( )
424380 delete playersRef . current [ id ]
425- delete playerTargetPositionsRef . current [ id ]
426381 }
427382 } )
428383
0 commit comments