Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Client/game_sa/CWaterManagerSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,12 @@ float CWaterManagerSA::GetWaveLevel()
return *(float*)VAR_WaveLevel;
}

void CWaterManagerSA::SetWavePhase(DWORD milliseconds)
{
const DWORD gameTime = *reinterpret_cast<const DWORD*>(0xB7CB84);
*reinterpret_cast<DWORD*>(0xC228A4) = gameTime - milliseconds;
}

void CWaterManagerSA::SetWaveLevel(float fWaveLevel)
{
if (fWaveLevel >= 0.0f)
Expand Down
1 change: 1 addition & 0 deletions Client/game_sa/CWaterManagerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class CWaterManagerSA : public CWaterManager

float GetWaveLevel();
void SetWaveLevel(float fWaveLevel);
void SetWavePhase(DWORD milliseconds) override;
void SetWaterDrawnLast(bool bEnable);
bool IsWaterDrawnLast();

Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3923,6 +3923,9 @@ void CClientGame::PreRenderSkyHandler()

void CClientGame::PreWeatherUpdateHandler()
{
// Refresh even after a long frame or pause, using the shared real-time phase.
m_pManager->GetWaterManager()->UpdateWavePhase();

// Fix #4803: Set MTA's weather types and zero InterpolationValue BEFORE
// CWeather::Update runs. Zeroing InterpolationValue prevents the wrap branch
// from ever firing (engine_interp >= 0 is always true), so the engine computes
Expand Down
36 changes: 36 additions & 0 deletions Client/mods/deathmatch/logic/CClientWaterManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,45 @@
*****************************************************************************/

#include "StdInc.h"
#include <algorithm>
#include <cmath>

using std::list;

void CClientWaterManager::ReceiveWaveSync(NetBitStreamInterface& stream)
{
unsigned int timeLow{}, timeHigh{};
if (!stream.Read(timeLow) || !stream.Read(timeHigh))
return;

const auto serverTime = (static_cast<std::uint64_t>(timeHigh) << 32) | timeLow;
if (serverTime <= m_lastWaveServerTime)
return;

const auto now = GetTickCount64_();
m_waveTargetTimeOffset = static_cast<double>(serverTime) + std::max(0, g_pNet->GetPing()) * 0.5 - static_cast<double>(now);
if (!m_lastWaveServerTime)
{
m_waveTimeOffset = m_waveTargetTimeOffset;
m_lastWaveUpdate = now;
}
m_lastWaveServerTime = serverTime;
}

void CClientWaterManager::UpdateWavePhase()
{
if (!m_lastWaveServerTime)
return;

const auto now = GetTickCount64_();
const double correction = std::max(0LL, now - m_lastWaveUpdate) * 0.02;
m_waveTimeOffset += std::clamp(m_waveTargetTimeOffset - m_waveTimeOffset, -correction, correction);
m_lastWaveUpdate = now;

const auto phase = static_cast<DWORD>(std::fmod(static_cast<double>(now) + m_waveTimeOffset, 105000.0));
g_pGame->GetWaterManager()->SetWavePhase(phase);
}

CClientWaterManager::CClientWaterManager(CClientManager* pManager)
{
m_pManager = pManager;
Expand Down
7 changes: 7 additions & 0 deletions Client/mods/deathmatch/logic/CClientWaterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class CClientWaterManager;

#include "CClientManager.h"
#include "CClientWater.h"
#include <cstdint>

class CClientWaterManager
{
Expand All @@ -35,6 +36,8 @@ class CClientWaterManager

float GetWaveLevel();
void SetWaveLevel(float fWaveLevel);
void UpdateWavePhase();
void ReceiveWaveSync(NetBitStreamInterface& stream);

unsigned short GetDimension() { return m_usDimension; };
void SetDimension(unsigned short usDimension);
Expand All @@ -54,4 +57,8 @@ class CClientWaterManager
std::list<CClientWater*> m_List;
bool m_bDontRemoveFromList;
unsigned short m_usDimension;
std::uint64_t m_lastWaveServerTime = 0;
long long m_lastWaveUpdate = 0;
double m_waveTimeOffset = 0;
double m_waveTargetTimeOffset = 0;
};
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CNetAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ bool CNetAPI::ProcessPacket(unsigned char bytePacketID, NetBitStreamInterface& B
m_bVehicleLastReturn = false;
}

if (BitStream.Can(eBitStreamVersion::WaterWaveSync))
m_pManager->GetWaterManager()->ReceiveWaveSync(BitStream);

// Remember the last return sync time
m_ulLastSyncReturnTime = CClientTime::GetTime();
m_bStoredReturnSync = true;
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2690,6 +2690,9 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream)
bitStream.ReadBit(bOcclusionsEnabled);

g_pGame->GetWorld()->SetOcclusionsEnabled(bOcclusionsEnabled);

if (bitStream.Can(eBitStreamVersion::WaterWaveSync))
g_pClientGame->GetManager()->GetWaterManager()->ReceiveWaveSync(bitStream);
}

void CPacketHandler::Packet_PartialPacketInfo(NetBitStreamInterface& bitStream)
Expand Down
1 change: 1 addition & 0 deletions Client/sdk/game/CWaterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ class CWaterManager
virtual void UndoChanges(void* pChangeSource = NULL) = 0;
virtual void RebuildIndex() = 0; // Call this after moving a polygon's vertices
virtual void Reset() = 0; // Reset all water to SA default
virtual void SetWavePhase(DWORD milliseconds) = 0;
};
7 changes: 7 additions & 0 deletions Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,5 +357,12 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const
bool bOcclusionsEnabled = g_pGame->GetOcclusionsEnabled();
BitStream.WriteBit(bOcclusionsEnabled);

if (BitStream.Can(eBitStreamVersion::WaterWaveSync))
{
const auto now = static_cast<std::uint64_t>(GetTickCount64_());
BitStream.Write(static_cast<unsigned int>(now));
BitStream.Write(static_cast<unsigned int>(now >> 32));
}

return true;
}
7 changes: 7 additions & 0 deletions Server/mods/deathmatch/logic/packets/CReturnSyncPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ bool CReturnSyncPacket::Write(NetBitStreamInterface& BitStream) const
BitStream.Write(&position);
}

if (BitStream.Can(eBitStreamVersion::WaterWaveSync))
{
const auto now = static_cast<std::uint64_t>(GetTickCount64_());
BitStream.Write(static_cast<unsigned int>(now));
BitStream.Write(static_cast<unsigned int>(now >> 32));
}

return true;
}

Expand Down
4 changes: 4 additions & 0 deletions Shared/sdk/net/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ enum class eBitStreamVersion : unsigned short
// YYYY-MM-DD
// Name,

// Server time in map info and return sync for water wave phase.
// 2026-09-12
WaterWaveSync,

// This allows us to automatically increment the BitStreamVersion when things are added to this enum.
// Make sure you only add things above this comment.
Next,
Expand Down
Loading