Fix vehicle serialization during races (#1122)

* Fix vehicle serialization during races

- Add missing frame stats reading
- correct the inversion of rotation
- correct serialization order
- use proper dirty flags

Tested that racers are no longer sideways on certain vertical slopes and stay in sync throughout the whole race.

* Update ClientPackets.cpp

* Update ClientPackets.cpp

* Update VehiclePhysicsComponent.h
This commit is contained in:
David Markowitz 2023-06-20 07:19:21 -07:00 committed by GitHub
parent 2d31b7e4bb
commit 132d31d3ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 12 deletions

View File

@ -19,34 +19,47 @@ VehiclePhysicsComponent::~VehiclePhysicsComponent() {
} }
void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) { void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (pos == m_Position) return;
m_DirtyPosition = true;
m_Position = pos; m_Position = pos;
} }
void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) { void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (rot == m_Rotation) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Rotation = rot; m_Rotation = rot;
} }
void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (vel == m_Velocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Velocity = vel; m_Velocity = vel;
} }
void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (vel == m_AngularVelocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_AngularVelocity = vel; m_AngularVelocity = vel;
} }
void VehiclePhysicsComponent::SetIsOnGround(bool val) { void VehiclePhysicsComponent::SetIsOnGround(bool val) {
if (val == m_IsOnGround) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnGround = val; m_IsOnGround = val;
} }
void VehiclePhysicsComponent::SetIsOnRail(bool val) { void VehiclePhysicsComponent::SetIsOnRail(bool val) {
if (val == m_IsOnRail) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnRail = val; m_IsOnRail = val;
} }
void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
if (m_RemoteInputInfo == remoteInputInfo) return;
this->m_RemoteInputInfo = remoteInputInfo;
m_DirtyRemoteInput = true;
}
void VehiclePhysicsComponent::SetDirtyPosition(bool val) { void VehiclePhysicsComponent::SetDirtyPosition(bool val) {
m_DirtyPosition = val; m_DirtyPosition = val;
} }
@ -63,9 +76,15 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (bIsInitialUpdate || m_DirtyPosition) { if (bIsInitialUpdate || m_DirtyPosition) {
outBitStream->Write(m_Position); m_DirtyPosition = false;
outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z);
outBitStream->Write(m_Rotation); outBitStream->Write(m_Rotation.x);
outBitStream->Write(m_Rotation.y);
outBitStream->Write(m_Rotation.z);
outBitStream->Write(m_Rotation.w);
outBitStream->Write(m_IsOnGround); outBitStream->Write(m_IsOnGround);
outBitStream->Write(m_IsOnRail); outBitStream->Write(m_IsOnRail);
@ -73,20 +92,33 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity); outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
if (bIsInitialUpdate || m_DirtyVelocity) { if (bIsInitialUpdate || m_DirtyVelocity) {
outBitStream->Write(m_Velocity); outBitStream->Write(m_Velocity.x);
outBitStream->Write(m_Velocity.y);
outBitStream->Write(m_Velocity.z);
m_DirtyVelocity = false;
} }
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity); outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
if (bIsInitialUpdate || m_DirtyAngularVelocity) { if (bIsInitialUpdate || m_DirtyAngularVelocity) {
outBitStream->Write(m_AngularVelocity); outBitStream->Write(m_AngularVelocity.x);
outBitStream->Write(m_AngularVelocity.y);
outBitStream->Write(m_AngularVelocity.z);
m_DirtyAngularVelocity = false;
} }
outBitStream->Write0(); outBitStream->Write0(); // local_space_info. TODO: Implement this
outBitStream->Write0(); outBitStream->Write(m_DirtyRemoteInput || bIsInitialUpdate); // remote_input_info
if (m_DirtyRemoteInput || bIsInitialUpdate) {
outBitStream->Write(m_RemoteInputInfo.m_RemoteInputX);
outBitStream->Write(m_RemoteInputInfo.m_RemoteInputY);
outBitStream->Write(m_RemoteInputInfo.m_IsPowersliding);
outBitStream->Write(m_RemoteInputInfo.m_IsModified);
m_DirtyRemoteInput = false;
}
outBitStream->Write(0.0f); outBitStream->Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race.
if (!bIsInitialUpdate) { if (!bIsInitialUpdate) {
outBitStream->Write0(); outBitStream->Write0();
@ -95,7 +127,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write<uint8_t>(m_EndBehavior); outBitStream->Write<uint8_t>(m_EndBehavior);
outBitStream->Write1(); outBitStream->Write1(); // is input locked?
} }
outBitStream->Write0(); outBitStream->Write0();
@ -104,7 +136,6 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
void VehiclePhysicsComponent::Update(float deltaTime) { void VehiclePhysicsComponent::Update(float deltaTime) {
if (m_SoftUpdate > 5) { if (m_SoftUpdate > 5) {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
m_SoftUpdate = 0; m_SoftUpdate = 0;
} else { } else {
m_SoftUpdate += deltaTime; m_SoftUpdate += deltaTime;

View File

@ -5,6 +5,24 @@
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
struct RemoteInputInfo {
void operator=(const RemoteInputInfo& other) {
m_RemoteInputX = other.m_RemoteInputX;
m_RemoteInputY = other.m_RemoteInputY;
m_IsPowersliding = other.m_IsPowersliding;
m_IsModified = other.m_IsModified;
}
bool operator==(const RemoteInputInfo& other) {
return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified;
}
float m_RemoteInputX;
float m_RemoteInputY;
bool m_IsPowersliding;
bool m_IsModified;
};
/** /**
* Physics component for vehicles. * Physics component for vehicles.
*/ */
@ -94,6 +112,7 @@ public:
void SetDirtyPosition(bool val); void SetDirtyPosition(bool val);
void SetDirtyVelocity(bool val); void SetDirtyVelocity(bool val);
void SetDirtyAngularVelocity(bool val); void SetDirtyAngularVelocity(bool val);
void SetRemoteInputInfo(const RemoteInputInfo&);
private: private:
bool m_DirtyPosition; bool m_DirtyPosition;
@ -110,4 +129,6 @@ private:
float m_SoftUpdate = 0; float m_SoftUpdate = 0;
uint32_t m_EndBehavior; uint32_t m_EndBehavior;
RemoteInputInfo m_RemoteInputInfo;
bool m_DirtyRemoteInput;
}; };

View File

@ -136,6 +136,33 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
inStream.Read(angVelocity.z); inStream.Read(angVelocity.z);
} }
// TODO figure out how to use these. Ignoring for now, but reading in if they exist.
bool hasLocalSpaceInfo{};
LWOOBJID objectId{};
NiPoint3 localSpacePosition{};
bool hasLinearVelocity{};
NiPoint3 linearVelocity{};
if (inStream.Read(hasLocalSpaceInfo) && hasLocalSpaceInfo) {
inStream.Read(objectId);
inStream.Read(localSpacePosition.x);
inStream.Read(localSpacePosition.y);
inStream.Read(localSpacePosition.z);
if (inStream.Read(hasLinearVelocity) && hasLinearVelocity) {
inStream.Read(linearVelocity.x);
inStream.Read(linearVelocity.y);
inStream.Read(linearVelocity.z);
}
}
bool hasRemoteInputInfo{};
RemoteInputInfo remoteInput{};
if (inStream.Read(hasRemoteInputInfo) && hasRemoteInputInfo) {
inStream.Read(remoteInput.m_RemoteInputX);
inStream.Read(remoteInput.m_RemoteInputY);
inStream.Read(remoteInput.m_IsPowersliding);
inStream.Read(remoteInput.m_IsModified);
}
bool updateChar = true; bool updateChar = true;
if (possessorComponent != nullptr) { if (possessorComponent != nullptr) {
@ -150,9 +177,6 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>(); auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>();
if (vehiclePhysicsComponent != nullptr) { if (vehiclePhysicsComponent != nullptr) {
// This is flipped for whatever reason
rotation = NiQuaternion(rotation.z, rotation.y, rotation.x, rotation.w);
vehiclePhysicsComponent->SetPosition(position); vehiclePhysicsComponent->SetPosition(position);
vehiclePhysicsComponent->SetRotation(rotation); vehiclePhysicsComponent->SetRotation(rotation);
vehiclePhysicsComponent->SetIsOnGround(onGround); vehiclePhysicsComponent->SetIsOnGround(onGround);
@ -161,6 +185,7 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag);
vehiclePhysicsComponent->SetAngularVelocity(angVelocity); vehiclePhysicsComponent->SetAngularVelocity(angVelocity);
vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag);
vehiclePhysicsComponent->SetRemoteInputInfo(remoteInput);
} else { } else {
// Need to get the mount's controllable physics // Need to get the mount's controllable physics
auto* controllablePhysicsComponent = possassableEntity->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = possassableEntity->GetComponent<ControllablePhysicsComponent>();