Skip to content

Commit

Permalink
Implemented simple player movement version control to avoid desync
Browse files Browse the repository at this point in the history
Bump minor API version
  • Loading branch information
s1lentq committed Aug 16, 2024
1 parent 4e1cb10 commit 8b6d659
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 12 deletions.
1 change: 1 addition & 0 deletions regamedll/dlls/cdll_dll.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const int DEFAULT_FOV = 90; // the default field of view
#define PLAYER_PREVENT_DUCK BIT(4)
#define PLAYER_PREVENT_CLIMB BIT(5) // The player can't climb ladder
#define PLAYER_PREVENT_JUMP BIT(6)
#define PLAYER_PREVENT_DDUCK BIT(7)

#define MENU_KEY_1 BIT(0)
#define MENU_KEY_2 BIT(1)
Expand Down
34 changes: 34 additions & 0 deletions regamedll/dlls/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3299,6 +3299,26 @@ void EXT_FUNC InternalCommand(edict_t *pEntity, const char *pcmd, const char *pa
}
}
#endif

#ifdef REGAMEDLL_ADD
// Request from client for the given version of player movement control, if any
else if (FStrEq(pcmd, "cl_pmove_version"))
{
// cl_pmove_version <num>
if (CMD_ARGC_() < 2)
return; // invalid

PlayerMovementVersion &playerMovementVersion = pPlayer->CSPlayer()->m_MovementVersion;
playerMovementVersion.Set(parg1);

// If the client's requested movement version is newer, enforce it to the available one
if (playerMovementVersion.IsGreaterThan(PM_VERSION))
{
playerMovementVersion.Set(PM_VERSION); // reset to available version
CLIENT_COMMAND(pEntity, "cl_pmove_version %s\n", playerMovementVersion.ToString());
}
}
#endif
else
{
if (g_pGameRules->ClientCommand_DeadOrAlive(GetClassPtr<CCSPlayer>((CBasePlayer *)pev), pcmd))
Expand Down Expand Up @@ -3727,6 +3747,20 @@ void EXT_FUNC ServerDeactivate()

void EXT_FUNC ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
{
#ifdef REGAMEDLL_ADD
//
// Tells clients which version of player movement (pmove) the server is using
//
// In GoldSrc, both the server and clients handle player movement using shared code.
// If the server changes how movement works, due to improvements or bugfixes, it can mess up
// the client's movement prediction, causing desync. To avoid this, the server can tell clients what
// version of the movement code it's using. Clients that don't recognize or respond to this version
// will be treated as using the previous behavior, and the server will handle them accordingly.
// Clients that do recognize it will let the server know, so everything stays in sync.
//
SET_KEY_VALUE(GET_INFO_BUFFER(pEdictList), "pmove", PM_ServerVersion());
#endif

int i;
CBaseEntity *pClass;

Expand Down
70 changes: 69 additions & 1 deletion regamedll/pm_shared/pm_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,74 @@ typedef struct physent_s

} physent_t;

#define PM_VERSION_MAJOR 1
#define PM_VERSION_MINOR 0
#define PM_VERSION_PATCH 0
#define PM_VERSION PM_VERSION_MAJOR, PM_VERSION_MINOR, PM_VERSION_PATCH

#define PM_VERSION_STRINGIZE(x) #x
#define PM_VERSION_STRING(major,minor,patch) \
(patch == 0 ?\
PM_VERSION_STRINGIZE(major) "." PM_VERSION_STRINGIZE(minor) :\
PM_VERSION_STRINGIZE(major) "." PM_VERSION_STRINGIZE(minor) "." PM_VERSION_STRINGIZE(patch))

// Control version of the player movement system
struct PlayerMovementVersion {
uint8_t major, minor, patch;
uint32_t u32;

PlayerMovementVersion() {
Set(0, 0, 0);
}

PlayerMovementVersion(uint32_t majorVersion, uint32_t minorVersion, uint32_t patchVersion = 0) {
Set(majorVersion, minorVersion, patchVersion);
}

inline void Set(uint32_t majorVersion, uint32_t minorVersion, uint32_t patchVersion = 0)
{
major = majorVersion;
minor = minorVersion;
patch = patchVersion;
u32 = (major << 16) | (minor << 8) | patch;
}

inline void Set(const char *version)
{
if (!version)
return;

major = minor = patch = u32 = 0;
int result = sscanf(version, "%hhu.%hhu.%hhu", &major, &minor, &patch);
if (result < 1)
return; // major invalid

u32 = (major << 16) | (minor << 8) | patch;
}

// Compares if the current version is less than the given version
inline bool IsLessThan(uint32_t major, uint32_t minor, uint32_t patch = 0) const {
return u32 < ((major << 16) | (minor << 8) | patch);
}

// Compares if the current version is greater than the given version
inline bool IsGreaterThan(uint32_t major, uint32_t minor, uint32_t patch = 0) const {
return u32 > ((major << 16) | (minor << 8) | patch);
}

// Compares if the current version is greater than or equal to the given version
inline bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch = 0) const {
return !IsLessThan(major, minor, patch);
}

const char *ToString() const {
static char string[14];
int len = Q_snprintf(string, sizeof(string), "%u.%u.%u", major, minor, patch);
if (patch == 0 && len > 2) string[len - 2] = '\0';
return string;
}
};

typedef struct playermove_s
{
int player_index; // So we don't try to run the PM_CheckStuck nudging too quickly.
Expand All @@ -111,7 +179,7 @@ typedef struct playermove_s
qboolean bInDuck; // In process of ducking or ducked already?
int flTimeStepSound; // For walking/falling
// Next time we can play a step sound
int iStepLeft;
qboolean iStepLeft;
float flFallVelocity;
vec3_t punchangle;
float flSwimTime;
Expand Down
26 changes: 17 additions & 9 deletions regamedll/pm_shared/pm_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ void PM_WalkMove()

#ifdef REGAMEDLL_ADD
// Player can speed up the run if '+speed' button is pressed
if ((pmove->cmd.buttons & IN_RUN) && pmove->fuser3 > 0)
if (pmoveplayer->m_MovementVersion.IsAtLeast(1, 0) && (pmove->cmd.buttons & IN_RUN) && pmove->fuser3 > 0)
{
fmove *= pmove->fuser3;
smove *= pmove->fuser3;
Expand Down Expand Up @@ -1450,7 +1450,7 @@ void PM_CategorizePosition()

// Do not stick to the ground of an OBSERVER or NOCLIP mode
#ifdef REGAMEDLL_FIXES
if (pmove->movetype == MOVETYPE_NOCLIP || pmove->movetype == MOVETYPE_NONE)
if (pmoveplayer->m_MovementVersion.IsAtLeast(1, 0) && (pmove->movetype == MOVETYPE_NOCLIP || pmove->movetype == MOVETYPE_NONE))
{
pmove->onground = -1;
return;
Expand Down Expand Up @@ -1704,7 +1704,7 @@ void PM_SpectatorMove()

#ifdef REGAMEDLL_ADD
// Observer can accelerate in air if '+speed' button is pressed
if (pmove->cmd.buttons & IN_RUN)
if (pmoveplayer->m_MovementVersion.IsAtLeast(1, 0) && pmove->cmd.buttons & IN_RUN)
{
float flAirAccelerate = (pmove->fuser3 > 0.0f) ? pmove->fuser3 : max(pmove->movevars->airaccelerate / 100.0f, 7.0f);
fmove *= flAirAccelerate;
Expand Down Expand Up @@ -1737,12 +1737,15 @@ void PM_SpectatorMove()

addspeed = wishspeed - currentspeed;

#ifndef REGAMEDLL_FIXES
if (addspeed <= 0)
return;
#else
if (addspeed > 0)
{
#ifdef REGAMEDLL_FIXES
if (pmoveplayer->m_MovementVersion.IsLessThan(1, 0))
#endif
return;
}

if (addspeed > 0)
{
accelspeed = pmove->movevars->accelerate * pmove->frametime * wishspeed;
if (accelspeed > addspeed)
Expand Down Expand Up @@ -1844,7 +1847,7 @@ LINK_HOOK_VOID_CHAIN2(PM_UnDuck)
void EXT_FUNC __API_HOOK(PM_UnDuck)()
{
#ifdef REGAMEDLL_ADD
if (unduck_method.value)
if (unduck_method.value || (pmove->iuser3 & PLAYER_PREVENT_DDUCK))
#endif
{
#ifdef REGAMEDLL_FIXES
Expand Down Expand Up @@ -2365,7 +2368,7 @@ void PM_NoClip()

#ifdef REGAMEDLL_ADD
// Player with noclip can accelerate in air if '+speed' button is pressed
if ((pmove->cmd.buttons & IN_RUN) && pmove->fuser3 > 0)
if (pmoveplayer->m_MovementVersion.IsAtLeast(1, 0) && (pmove->cmd.buttons & IN_RUN) && pmove->fuser3 > 0)
{
float flAirAccelerate = pmove->fuser3;
fmove *= flAirAccelerate;
Expand Down Expand Up @@ -3379,3 +3382,8 @@ void EXT_FUNC __API_HOOK(PM_Init)(struct playermove_s *ppmove)

pm_shared_initialized = TRUE;
}

const char *PM_ServerVersion()
{
return PM_VERSION_STRING(PM_VERSION_MAJOR, PM_VERSION_MINOR, PM_VERSION_PATCH);
}
2 changes: 2 additions & 0 deletions regamedll/pm_shared/pm_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,6 @@ void PM_AirAccelerate_OrigFunc(vec_t *wishdir, float wishspeed, float accel);
void PM_AirMove(int playerIndex = 0);
#endif

const char *PM_ServerVersion();

extern struct playermove_s *pmove;
3 changes: 3 additions & 0 deletions regamedll/public/regamedll/API/CSPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ class CCSPlayer: public CCSMonster {

int m_iGibDamageThreshold; // negative health to reach to gib player
usercmd_t m_LastCmd;

// Player movement version control
PlayerMovementVersion m_MovementVersion;
};

// Inlines
Expand Down
2 changes: 1 addition & 1 deletion regamedll/public/regamedll/regamedll_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
#include <API/CSInterfaces.h>

#define REGAMEDLL_API_VERSION_MAJOR 5
#define REGAMEDLL_API_VERSION_MINOR 27
#define REGAMEDLL_API_VERSION_MINOR 28

// CBasePlayer::Spawn hook
typedef IHookChainClass<void, class CBasePlayer> IReGameHook_CBasePlayer_Spawn;
Expand Down
2 changes: 1 addition & 1 deletion regamedll/version/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
#pragma once

#define VERSION_MAJOR 5
#define VERSION_MINOR 27
#define VERSION_MINOR 28
#define VERSION_MAINTENANCE 0

0 comments on commit 8b6d659

Please sign in to comment.