Skip to content

Add player camera pitch synchronization - #5315

Open
imfelipedev wants to merge 1 commit into
multitheftauto:masterfrom
imfelipedev:camera-sync
Open

Add player camera pitch synchronization#5315
imfelipedev wants to merge 1 commit into
multitheftauto:masterfrom
imfelipedev:camera-sync

Conversation

@imfelipedev

Copy link
Copy Markdown
Contributor

Summary

This PR adds camera pitch synchronization for remote players through player pure sync and exposes it through getPedCameraRotation.

Previously, getPedCameraRotation only exposed the horizontal camera rotation (yaw). The client already sends its camera orientation to the server as part of player pure sync, including the vertical camera angle, but that information was not forwarded to other clients.

This change reuses the existing camera forward vector on the server, derives the pitch from it, and sends it as an 8-bit synchronized value.

The existing behavior of getPedCameraRotation is preserved:

local yaw = getPedCameraRotation(player)

The synchronized pitch can be requested explicitly:

local yaw, pitch = getPedCameraRotation(player, true)

This keeps existing resources backward compatible while allowing scripts to use the remote player's vertical camera direction.

Motivation

On roleplay servers, head-tracking scripts commonly need each player's camera direction to make nearby players' heads follow where they are looking, especially when looking up or down.

Since remote camera pitch is not currently exposed to scripts, resources usually have to implement their own synchronization by sending camera data to the server with triggerServerEvent and then relaying it back to nearby clients.

This requires custom Lua events and additional network traffic for camera information that is already available in the engine's synchronization path.

By synchronizing and exposing the pitch natively, these resources can rely on the existing player sync instead of maintaining a separate client-to-server-to-client synchronization layer.

Implementation

  • Adds SCameraPitchSync using SFloatAsBitsSync<8>.
  • Synchronizes camera pitch through player pure sync.
  • Stores the latest synchronized pitch on CClientPlayer.
  • Extends getPedCameraRotation with an optional pitch return value.
  • Increments _NETCODE_VERSION because the network message layout changes.
  • Reuses the camera orientation already sent by the client; no additional client-to-server data is introduced.

The pitch is represented in degrees in the [-90, 90] range.

Testing

Tested with multiple clients while:

  • standing still;
  • walking and running;
  • rotating the camera horizontally;
  • looking up and down;
  • rapidly changing camera direction.

Remote players correctly receive both yaw and pitch, while the original single-return-value behavior remains unchanged.

1F0B3B4E-EC11-466A-AC60-6E3E3A40710F 0C9EE57A-032F-46FE-80FA-B81D4291F280

This test resource was used to verify the synchronization by making nearby remote players look in the same direction as their camera.

Network impact

This does not introduce a new packet or an additional client-to-server message.

The pitch is appended to the existing player pure sync data using 8 bits, resulting in one additional byte per synchronized player pure sync packet.

Copilot AI lite review requested due to automatic review settings September 4, 2026 05:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new pitch API/storage introduces unit ambiguity (degrees vs radians) and a few conversion/convention inconsistencies that should be clarified/fixed before merge.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR extends the existing player pure-sync path to forward remote players’ camera pitch (derived from the already-sent camera forward vector) to other clients, and exposes it to Lua via an optional second return value from getPedCameraRotation.

Changes:

  • Added an 8-bit SCameraPitchSync and appended it to server→client player puresync payloads (with _NETCODE_VERSION bump).
  • Client now reads/stores the synchronized pitch on CClientPlayer.
  • Lua getPedCameraRotation(ped, true) returns (yaw, pitch) while preserving the legacy single-return (yaw) behavior.

Note for merge/commit message: please include the motivation (RP head-tracking use-case), the netcode layout change (_NETCODE_VERSION bump), and how you validated with multiple clients; also ensure C++ formatting is run (e.g. ./utils/clang-format.ps1) before finalizing.

File summaries
File Description
Shared/sdk/version.h Bumps netcode version due to puresync layout change.
Shared/sdk/net/SyncStructures.h Adds SCameraPitchSync as 8-bit float-in-range sync type.
Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp Appends pitch (derived from cam forward) to outgoing player puresync.
Server/mods/deathmatch/logic/net/CSimPlayerPuresyncPacket.cpp Appends pitch for simulated puresync packets as well.
Client/mods/deathmatch/logic/CNetAPI.cpp Reads synced pitch from puresync and stores it on CClientPlayer.
Client/mods/deathmatch/logic/CClientPlayer.h Adds storage/accessors for latest synced camera pitch.
Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp Extends getPedCameraRotation to optionally return pitch.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +95 to +96
float GetCameraPitch() const { return m_fCameraPitch; }
void SetCameraPitch(float fPitch) { m_fCameraPitch = fPitch; }
Comment on lines +1649 to +1652

const CVector& vecCamFwd = cameraMatrix.vFront;
fPitch = atan2(vecCamFwd.fZ, DistanceBetweenPoints2D(CVector(), vecCamFwd)) * (180.0f / PI);
}
Comment on lines +260 to +262
SCameraPitchSync cameraPitch;
cameraPitch.data.fValue = atan2(m_Cache.vecCamFwd.fZ, DistanceBetweenPoints2D(CVector(), m_Cache.vecCamFwd)) * (180.0f / PI);
BitStream.Write(&cameraPitch);
Comment on lines +465 to +467
SCameraPitchSync cameraPitch;
cameraPitch.data.fValue = atan2(vecCamFwd.fZ, DistanceBetweenPoints2D(CVector(), vecCamFwd)) * (180.0f / PI);
BitStream.Write(&cameraPitch);
@DevGustavoo

Copy link
Copy Markdown

nice one!

@FileEX FileEX added the enhancement New feature or request label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants