fix(complement-race): ensure continuous position broadcasting during train movement

Fixed an issue where ghost trains only updated when players stopped moving.

Root cause: clientPosition in useEffect dependency array caused the
position broadcasting interval to restart on every position change,
creating gaps in broadcasts during continuous movement.

Solution:
- Use useRef to track latest clientPosition without triggering effect
- Keep ref synced with position via separate useEffect
- Read position from ref inside interval callback
- Remove clientPosition from broadcasting useEffect dependencies

Now positions broadcast smoothly every 200ms regardless of movement state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-10-22 13:12:43 -05:00
parent 543675340d
commit df60824f37
1 changed files with 11 additions and 10 deletions

View File

@ -325,6 +325,9 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
const [clientMomentum, setClientMomentum] = useState(10) // Start at 10 for gentle push
const [clientPosition, setClientPosition] = useState(0)
const [clientPressure, setClientPressure] = useState(0)
// Ref to track latest position for broadcasting (avoids recreating interval on every position change)
const clientPositionRef = useRef(clientPosition)
const [clientAIRacers, setClientAIRacers] = useState<
Array<{
id: string
@ -564,31 +567,29 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
}
}, [multiplayerState.currentRoute, compatibleState.style, multiplayerState.passengers.length])
// Keep position ref in sync with latest position
useEffect(() => {
clientPositionRef.current = clientPosition
}, [clientPosition])
// Broadcast position to server for multiplayer ghost trains
useEffect(() => {
if (!compatibleState.isGameActive || compatibleState.style !== 'sprint' || !localPlayerId) {
return
}
// Send position update every 200ms
// Send position update every 200ms (reads from ref to avoid restarting interval)
const interval = setInterval(() => {
sendMove({
type: 'UPDATE_POSITION',
playerId: localPlayerId,
userId: viewerId || '',
data: { position: clientPosition },
data: { position: clientPositionRef.current },
} as ComplementRaceMove)
}, 200)
return () => clearInterval(interval)
}, [
compatibleState.isGameActive,
compatibleState.style,
clientPosition,
localPlayerId,
viewerId,
sendMove,
])
}, [compatibleState.isGameActive, compatibleState.style, localPlayerId, viewerId, sendMove])
// Keep lastLogRef for future debugging needs
// (removed debug logging)