From 386935f5cfc13393edd8da102dcd48a163e8d959 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 19 Jan 2024 22:05:53 +0300 Subject: [PATCH] Character: Incapsulate m_MoveRestrictions and m_Core Instead of leaking the m_MoveRestrictions to all classes which needs to adjust the Character velocity, add a setter which takes into account those restrictions. The shotgun bug replication requires an access to the Velocity without the restrictions applied (so we have to have this dirty setter). Expose only *const* CCharacterCore to force the setters usage and prevent incorrect write. --- .../client/prediction/entities/character.cpp | 40 ++++++++++++++++ .../client/prediction/entities/character.h | 13 ++++- .../client/prediction/entities/dragger.cpp | 3 +- src/game/client/prediction/entities/laser.cpp | 21 ++++---- src/game/client/prediction/gameworld.cpp | 13 ++--- src/game/server/ddracechat.cpp | 6 +-- src/game/server/ddracecommands.cpp | 7 ++- src/game/server/entities/character.cpp | 48 +++++++++++++++++-- src/game/server/entities/character.h | 16 +++++-- src/game/server/entities/dragger_beam.cpp | 3 +- src/game/server/entities/laser.cpp | 21 ++++---- src/game/server/gameworld.cpp | 7 +-- 12 files changed, 145 insertions(+), 53 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index d1c1570f1..0f46c97d0 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -546,6 +546,19 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } +void CCharacter::ReleaseHook() +{ + m_Core.SetHookedPlayer(-1); + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; +} + +void CCharacter::ResetHook() +{ + ReleaseHook(); + m_Core.m_HookPos = m_Core.m_Pos; +} + void CCharacter::ResetInput() { m_Input.m_Direction = 0; @@ -1105,6 +1118,33 @@ void CCharacter::GiveAllWeapons() } } +void CCharacter::ResetVelocity() +{ + m_Core.m_Vel = vec2(0, 0); +} + +// The method is needed only to reproduce 'shotgun bug' ddnet#5258 +// Use SetVelocity() instead. +void CCharacter::SetVelocity(const vec2 NewVelocity) +{ + m_Core.m_Vel = ClampVel(m_MoveRestrictions, NewVelocity); +} + +void CCharacter::SetRawVelocity(const vec2 NewVelocity) +{ + m_Core.m_Vel = NewVelocity; +} + +void CCharacter::AddVelocity(const vec2 Addition) +{ + SetVelocity(m_Core.m_Vel + Addition); +} + +void CCharacter::ApplyMoveRestrictions() +{ + m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel); +} + CTeamsCore *CCharacter::TeamsCore() { return GameWorld()->Teams(); diff --git a/src/game/client/prediction/entities/character.h b/src/game/client/prediction/entities/character.h index b424a8c9b..e197bf20b 100644 --- a/src/game/client/prediction/entities/character.h +++ b/src/game/client/prediction/entities/character.h @@ -51,6 +51,8 @@ public: void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void ReleaseHook(); + void ResetHook(); void ResetInput(); void FireWeapon(); @@ -60,6 +62,12 @@ public: void GiveNinja(); void RemoveNinja(); + void ResetVelocity(); + void SetVelocity(vec2 NewVelocity); + void SetRawVelocity(vec2 NewVelocity); + void AddVelocity(vec2 Addition); + void ApplyMoveRestrictions(); + bool m_IsLocal; CTeamsCore *TeamsCore(); @@ -81,7 +89,6 @@ public: int m_TileIndex; int m_TileFIndex; - int m_MoveRestrictions; bool m_LastRefillJumps; // Setters/Getters because i don't want to modify vanilla vars access modifiers @@ -91,7 +98,7 @@ public: void SetActiveWeapon(int ActiveWeap); CCharacterCore GetCore() { return m_Core; } void SetCore(CCharacterCore Core) { m_Core = Core; } - CCharacterCore *Core() { return &m_Core; } + const CCharacterCore *Core() const { return &m_Core; } bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; } void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; } int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; } @@ -145,6 +152,8 @@ private: int m_ReloadTimer; int m_AttackTick; + int m_MoveRestrictions; + // these are non-heldback inputs CNetObj_PlayerInput m_LatestPrevInput; CNetObj_PlayerInput m_LatestInput; diff --git a/src/game/client/prediction/entities/dragger.cpp b/src/game/client/prediction/entities/dragger.cpp index 07aec45af..189d7e1a0 100644 --- a/src/game/client/prediction/entities/dragger.cpp +++ b/src/game/client/prediction/entities/dragger.cpp @@ -124,8 +124,7 @@ void CDragger::DraggerBeamTick() // In the center of the dragger a tee does not experience speed-up else if(distance(pTarget->m_Pos, m_Pos) > 28) { - vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength); - pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp); + pTarget->AddVelocity(normalize(m_Pos - pTarget->m_Pos) * m_Strength); } } diff --git a/src/game/client/prediction/entities/laser.cpp b/src/game/client/prediction/entities/laser.cpp index 6b0fb9247..0b9954169 100644 --- a/src/game/client/prediction/entities/laser.cpp +++ b/src/game/client/prediction/entities/laser.cpp @@ -47,36 +47,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) m_Energy = -1; if(m_Type == WEAPON_SHOTGUN) { - vec2 Temp; - float Strength = GetTuning(m_TuneZone)->m_ShotgunStrength; + float Strength; + if(!m_TuneZone) + Strength = Tuning()->m_ShotgunStrength; + else + Strength = TuningList()[m_TuneZone].m_ShotgunStrength; + const vec2 &HitPos = pHit->Core()->m_Pos; if(!g_Config.m_SvOldLaser) { if(m_PrevPos != HitPos) { - Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength; - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp); + pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength); } else { - pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; + pHit->SetRawVelocity(StackedLaserShotgunBugSpeed); } } else if(g_Config.m_SvOldLaser && pOwnerChar) { if(pOwnerChar->Core()->m_Pos != HitPos) { - Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength; - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp); + pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength); } else { - pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; + pHit->SetRawVelocity(StackedLaserShotgunBugSpeed); } } else { - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, pHit->Core()->m_Vel); + // Re-apply move restrictions as a part of 'shotgun bug' reproduction + pHit->ApplyMoveRestrictions(); } } else if(m_Type == WEAPON_LASER) diff --git a/src/game/client/prediction/gameworld.cpp b/src/game/client/prediction/gameworld.cpp index a2fb70123..1b8817140 100644 --- a/src/game/client/prediction/gameworld.cpp +++ b/src/game/client/prediction/gameworld.cpp @@ -116,7 +116,7 @@ void CGameWorld::InsertEntity(CEntity *pEnt, bool Last) if(ID >= 0 && ID < MAX_CLIENTS) { m_apCharacters[ID] = pChar; - m_Core.m_apCharacters[ID] = pChar->Core(); + m_Core.m_apCharacters[ID] = &pChar->m_Core; } pChar->SetCoreWorld(this); } @@ -309,12 +309,9 @@ void CGameWorld::ReleaseHooked(int ClientID) CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER); for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) { - CCharacterCore *pCore = pChr->Core(); - if(pCore->HookedPlayer() == ClientID) + if(pChr->Core()->HookedPlayer() == ClientID && !pChr->IsSuper()) { - pCore->SetHookedPlayer(-1); - pCore->m_HookState = HOOK_RETRACTED; - pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + pChr->ReleaseHook(); } } } @@ -557,7 +554,7 @@ void CGameWorld::NetObjEnd() if(pHookedChar->m_MarkedForDestroy) { pHookedChar->m_Pos = pHookedChar->m_Core.m_Pos = pChar->m_Core.m_HookPos; - pHookedChar->m_Core.m_Vel = vec2(0, 0); + pHookedChar->ResetVelocity(); mem_zero(&pHookedChar->m_SavedInput, sizeof(pHookedChar->m_SavedInput)); pHookedChar->m_SavedInput.m_TargetY = -1; pHookedChar->m_KeepHooked = true; @@ -577,7 +574,7 @@ void CGameWorld::NetObjEnd() if(ID >= 0 && ID < MAX_CLIENTS) { m_apCharacters[ID] = pChar; - m_Core.m_apCharacters[ID] = pChar->Core(); + m_Core.m_apCharacters[ID] = &pChar->m_Core; } } } diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index e886c5f49..c95f8e8ef 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1593,7 +1593,7 @@ void CGameContext::ConTeleTo(IConsole::IResult *pResult, void *pUserData) // Teleport tee pSelf->Teleport(pCallingCharacter, Pos); pCallingCharacter->UnFreeze(); - pCallingCharacter->Core()->m_Vel = vec2(0, 0); + pCallingCharacter->ResetVelocity(); pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter); } @@ -1670,7 +1670,7 @@ void CGameContext::ConTeleXY(IConsole::IResult *pResult, void *pUserData) // Teleport tee pSelf->Teleport(pCallingCharacter, Pos); pCallingCharacter->UnFreeze(); - pCallingCharacter->Core()->m_Vel = vec2(0, 0); + pCallingCharacter->ResetVelocity(); pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter); } @@ -1722,7 +1722,7 @@ void CGameContext::ConTeleCursor(IConsole::IResult *pResult, void *pUserData) } pSelf->Teleport(pChr, Pos); pChr->UnFreeze(); - pChr->Core()->m_Vel = vec2(0, 0); + pChr->ResetVelocity(); pPlayer->m_LastTeleTee.Save(pChr); } diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index 27eacf94e..186e84eca 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -77,8 +77,7 @@ void CGameContext::MoveCharacter(int ClientID, int X, int Y, bool Raw) if(!pChr) return; - pChr->Core()->m_Pos.x += ((Raw) ? 1 : 32) * X; - pChr->Core()->m_Pos.y += ((Raw) ? 1 : 32) * Y; + pChr->Move(vec2((Raw ? 1 : 32) * X, Raw ? 1 : 32) * Y); pChr->m_DDRaceState = DDRACE_CHEAT; } @@ -348,7 +347,7 @@ void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData, void CGameContext::Teleport(CCharacter *pChr, vec2 Pos) { - pChr->Core()->m_Pos = Pos; + pChr->SetPosition(Pos); pChr->m_Pos = Pos; pChr->m_PrevPos = Pos; pChr->m_DDRaceState = DDRACE_CHEAT; @@ -412,7 +411,7 @@ void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData) } pSelf->Teleport(pChr, Pos); pChr->UnFreeze(); - pChr->Core()->m_Vel = vec2(0, 0); + pChr->SetVelocity(vec2(0, 0)); } } diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index ad405eb28..926b6e6a0 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -262,7 +262,7 @@ void CCharacter::HandleNinja() Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity); // reset velocity so the client doesn't predict stuff - m_Core.m_Vel = vec2(0.f, 0.f); + ResetVelocity(); // check if we Hit anything along the way { @@ -698,11 +698,16 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } -void CCharacter::ResetHook() +void CCharacter::ReleaseHook() { m_Core.SetHookedPlayer(-1); m_Core.m_HookState = HOOK_RETRACTED; m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; +} + +void CCharacter::ResetHook() +{ + ReleaseHook(); m_Core.m_HookPos = m_Core.m_Pos; } @@ -1640,7 +1645,7 @@ void CCharacter::HandleTiles(int Index) m_Core.m_Jumped = 0; m_Core.m_JumpedTotal = 0; } - m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel); + ApplyMoveRestrictions(); // handle switch tiles if(Collision()->GetSwitchType(MapIndex) == TILE_SWITCHOPEN && Team() != TEAM_SUPER && Collision()->GetSwitchNumber(MapIndex) > 0) @@ -2357,6 +2362,43 @@ CClientMask CCharacter::TeamMask() return Teams()->TeamMask(Team(), -1, GetPlayer()->GetCID()); } +void CCharacter::SetPosition(const vec2 &Position) +{ + m_Core.m_Pos = Position; +} + +void CCharacter::Move(vec2 RelPos) +{ + m_Core.m_Pos += RelPos; +} + +void CCharacter::ResetVelocity() +{ + m_Core.m_Vel = vec2(0, 0); +} + +void CCharacter::SetVelocity(vec2 NewVelocity) +{ + m_Core.m_Vel = ClampVel(m_MoveRestrictions, NewVelocity); +} + +// The method is needed only to reproduce 'shotgun bug' ddnet#5258 +// Use SetVelocity() instead. +void CCharacter::SetRawVelocity(vec2 NewVelocity) +{ + m_Core.m_Vel = NewVelocity; +} + +void CCharacter::AddVelocity(vec2 Addition) +{ + SetVelocity(m_Core.m_Vel + Addition); +} + +void CCharacter::ApplyMoveRestrictions() +{ + m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel); +} + void CCharacter::SwapClients(int Client1, int Client2) { const int HookedPlayer = m_Core.HookedPlayer(); diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 8f984b770..52c3a5e1a 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -60,6 +60,7 @@ public: void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void ReleaseHook(); void ResetHook(); void ResetInput(); void FireWeapon(); @@ -88,6 +89,15 @@ public: class CPlayer *GetPlayer() { return m_pPlayer; } CClientMask TeamMask(); + void SetPosition(const vec2 &Position); + void Move(vec2 RelPos); + + void ResetVelocity(); + void SetVelocity(vec2 NewVelocity); + void SetRawVelocity(vec2 NewVelocity); + void AddVelocity(vec2 Addition); + void ApplyMoveRestrictions(); + private: // player controlling this character class CPlayer *m_pPlayer; @@ -106,6 +116,8 @@ private: int m_ReloadTimer; int m_AttackTick; + int m_MoveRestrictions; + int m_DamageTaken; int m_EmoteType; @@ -201,8 +213,6 @@ public: int m_TileIndex; int m_TileFIndex; - int m_MoveRestrictions; - int64_t m_LastStartWarning; int64_t m_LastRescue; bool m_LastRefillJumps; @@ -226,7 +236,7 @@ public: void SetArmor(int Armor) { m_Armor = Armor; } CCharacterCore GetCore() { return m_Core; } void SetCore(CCharacterCore Core) { m_Core = Core; } - CCharacterCore *Core() { return &m_Core; } + const CCharacterCore *Core() const { return &m_Core; } bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; } void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; } int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; } diff --git a/src/game/server/entities/dragger_beam.cpp b/src/game/server/entities/dragger_beam.cpp index 26f03dab7..fb8f3f870 100644 --- a/src/game/server/entities/dragger_beam.cpp +++ b/src/game/server/entities/dragger_beam.cpp @@ -71,8 +71,7 @@ void CDraggerBeam::Tick() // In the center of the dragger a tee does not experience speed-up else if(distance(pTarget->m_Pos, m_Pos) > 28) { - vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength); - pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp); + pTarget->AddVelocity(normalize(m_Pos - pTarget->m_Pos) * m_Strength); } } diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 2907d5b95..be4ed55c1 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -44,9 +44,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) bool pDontHitSelf = g_Config.m_SvOldLaser || (m_Bounces == 0 && !m_WasTele); if(pOwnerChar ? (!pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (!pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : g_Config.m_SvHit) - pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner); + pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner); else - pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar); + pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar); if(!pHit || (pHit == pOwnerChar && g_Config.m_SvOldLaser) || (pHit != pOwnerChar && pOwnerChar ? (pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : !g_Config.m_SvHit)) return false; @@ -55,42 +55,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) m_Energy = -1; if(m_Type == WEAPON_SHOTGUN) { - vec2 Temp; - float Strength; if(!m_TuneZone) Strength = Tuning()->m_ShotgunStrength; else Strength = TuningList()[m_TuneZone].m_ShotgunStrength; - vec2 &HitPos = pHit->Core()->m_Pos; + const vec2 &HitPos = pHit->Core()->m_Pos; if(!g_Config.m_SvOldLaser) { if(m_PrevPos != HitPos) { - Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength; - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp); + pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength); } else { - pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; + pHit->SetRawVelocity(StackedLaserShotgunBugSpeed); } } else if(g_Config.m_SvOldLaser && pOwnerChar) { if(pOwnerChar->Core()->m_Pos != HitPos) { - Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength; - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp); + pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength); } else { - pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; + pHit->SetRawVelocity(StackedLaserShotgunBugSpeed); } } else { - pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, pHit->Core()->m_Vel); + // Re-apply move restrictions as a part of 'shotgun bug' reproduction + pHit->ApplyMoveRestrictions(); } } else if(m_Type == WEAPON_LASER) diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 9247cca88..053c0a6d4 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -361,12 +361,9 @@ void CGameWorld::ReleaseHooked(int ClientID) CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER); for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) { - CCharacterCore *pCore = pChr->Core(); - if(pCore->HookedPlayer() == ClientID && !pChr->IsSuper()) + if(pChr->Core()->HookedPlayer() == ClientID && !pChr->IsSuper()) { - pCore->SetHookedPlayer(-1); - pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; - pCore->m_HookState = HOOK_RETRACTED; + pChr->ReleaseHook(); } } }