3826: Respect TuneZone for characters r=def- a=TsFreddie

*I might be totally insane for doing this.
*The effect is only truly noticeable if you have high ping AND dropping packets.*

*clang-tidy's warning doesn't seems to relate to anything I've changed*


Now characters will try to respect TuneZones when ~~evolving and~~ predicting (like projectiles do). Since it is really important to keep TuningList as accurate as possible, I made a TuningList update logic so we try our best to update tunings only for the correct TuneZone (instead of constantly shoving current tuning into TuningList)

And each CCharacterCore now has its own Tunings to avoid any characters influencing GameWorld's tuning. Some server code has to be updated accordingly. I checked the code path, didn't notice anything that would make our server behave differently.

And I guess I should tag @trml myself this time.

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [ ] Considered possible null pointers and out of bounds array indexing
- [x] Changed no physics that affect existing maps (as far as I could tell)
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Freddie Wang <tsfreddiewang@gmail.com>
Co-authored-by: def <dennis@felsin9.de>
This commit is contained in:
bors[bot] 2021-05-23 08:16:57 +00:00 committed by GitHub
commit 47d63317aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 29 deletions

View file

@ -540,6 +540,15 @@ void CGameClient::OnReset()
m_LastNewPredictedTick[0] = -1;
m_LastNewPredictedTick[1] = -1;
m_LocalTuneZone[0] = 0;
m_LocalTuneZone[1] = 0;
m_ExpectingTuningForZone[0] = -1;
m_ExpectingTuningForZone[1] = -1;
m_ReceivedTuning[0] = false;
m_ReceivedTuning[1] = false;
InvalidateSnapshot();
for(auto &Client : m_aClients)
@ -782,6 +791,7 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy)
m_ServerMode = SERVERMODE_PURE;
m_ReceivedTuning[IsDummy ? !g_Config.m_ClDummy : g_Config.m_ClDummy] = true;
// apply new tuning
m_Tuning[IsDummy ? !g_Config.m_ClDummy : g_Config.m_ClDummy] = NewTuning;
return;
@ -2233,10 +2243,62 @@ void CGameClient::UpdatePrediction()
m_GameWorld.m_WorldConfig.m_IsSolo = !m_Snap.m_aCharacters[m_Snap.m_LocalClientID].m_HasExtendedData && !m_Tuning[g_Config.m_ClDummy].m_PlayerCollision && !m_Tuning[g_Config.m_ClDummy].m_PlayerHooking;
// update the tuning/tunezone at the local character position with the latest tunings received before the new snapshot
vec2 LocalCharPos = vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y);
m_GameWorld.m_Core.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
int TuneZone = 0;
if(m_GameWorld.m_WorldConfig.m_UseTuneZones)
TuneZone = Collision()->IsTune(Collision()->GetMapIndex(vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y)));
m_GameWorld.TuningList()[TuneZone] = m_GameWorld.m_Core.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
{
TuneZone = Collision()->IsTune(Collision()->GetMapIndex(LocalCharPos));
if(TuneZone != m_LocalTuneZone[g_Config.m_ClDummy])
{
// our tunezone changed, expecting tuning message
m_LocalTuneZone[g_Config.m_ClDummy] = m_ExpectingTuningForZone[g_Config.m_ClDummy] = TuneZone;
m_ExpectingTuningSince[g_Config.m_ClDummy] = 0;
}
if(m_ExpectingTuningForZone[g_Config.m_ClDummy] >= 0)
{
if(m_ReceivedTuning[g_Config.m_ClDummy])
{
dbg_msg("tunezone", "got tuning for zone %d", m_ExpectingTuningForZone[g_Config.m_ClDummy]);
m_GameWorld.TuningList()[m_ExpectingTuningForZone[g_Config.m_ClDummy]] = m_Tuning[g_Config.m_ClDummy];
m_ReceivedTuning[g_Config.m_ClDummy] = false;
m_ExpectingTuningForZone[g_Config.m_ClDummy] = -1;
}
else if(m_ExpectingTuningSince[g_Config.m_ClDummy] >= 5)
{
// if we are expecting tuning for more than 10 snaps (less than a quarter of a second)
// it is probably dropped or it was received out of order
// or applied to another tunezone.
// we need to fallback to current tuning to fix ourselves.
m_ExpectingTuningForZone[g_Config.m_ClDummy] = -1;
m_ExpectingTuningSince[g_Config.m_ClDummy] = 0;
m_ReceivedTuning[g_Config.m_ClDummy] = false;
dbg_msg("tunezone", "the tuning was missed");
}
else
{
// if we are expecting tuning and have not received one yet.
// do not update any tuning, so we don't apply it to the wrong tunezone.
dbg_msg("tunezone", "waiting for tuning for zone %d", m_ExpectingTuningForZone[g_Config.m_ClDummy]);
m_ExpectingTuningSince[g_Config.m_ClDummy]++;
}
}
else
{
// if we have processed what we need, and the tuning is still wrong due to out of order messege
// fix our tuning by using the current one
m_GameWorld.TuningList()[TuneZone] = m_Tuning[g_Config.m_ClDummy];
m_ExpectingTuningSince[g_Config.m_ClDummy] = 0;
m_ReceivedTuning[g_Config.m_ClDummy] = false;
}
}
else
{
m_GameWorld.TuningList()[0] = m_Tuning[g_Config.m_ClDummy];
}
// if ddnetcharacter is available, ignore server-wide tunings for hook and collision
if(m_Snap.m_aCharacters[m_Snap.m_LocalClientID].m_HasExtendedData)

View file

@ -238,6 +238,10 @@ public:
};
CSnapState m_Snap;
int m_LocalTuneZone[2];
bool m_ReceivedTuning[2];
int m_ExpectingTuningForZone[2];
int m_ExpectingTuningSince[2];
// client data
struct CClientData

View file

@ -864,9 +864,11 @@ void CCharacter::HandleTiles(int Index)
void CCharacter::HandleTuneLayer()
{
int CurrentIndex = Collision()->GetMapIndex(m_Pos);
SetTuneZone(GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(CurrentIndex) : 0);
SetTuneZone(GameWorld()->m_WorldConfig.m_UseTuneZones ? Collision()->IsTune(CurrentIndex) : 0);
m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = *GetTuning(m_TuneZone); // throw tunings (from specific zone if in a tunezone) into gamecore
if(m_IsLocal)
m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = *GetTuning(m_TuneZone); // throw tunings (from specific zone if in a tunezone) into gamecore if the character is local
m_Core.m_Tuning = *GetTuning(m_TuneZone);
}
void CCharacter::DDRaceTick()
@ -1003,6 +1005,7 @@ CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar,
CEntity(pGameWorld, CGameWorld::ENTTYPE_CHARACTER)
{
m_ID = ID;
m_IsLocal = false;
m_LastWeapon = WEAPON_HAMMER;
m_QueuedWeapon = -1;
@ -1072,6 +1075,7 @@ void CCharacter::ResetPrediction()
void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, bool IsLocal)
{
m_Core.Read((CNetObj_CharacterCore *)pChar);
m_IsLocal = IsLocal;
if(pExtended)
{
@ -1213,7 +1217,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
m_LastSnapWeapon = pChar->m_Weapon;
m_Alive = true;
SetTuneZone(GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0);
SetTuneZone(GameWorld()->m_WorldConfig.m_UseTuneZones ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0);
// set the current weapon
if(pChar->m_Weapon != WEAPON_NINJA)

View file

@ -64,6 +64,7 @@ public:
bool IsAlive() { return m_Alive; }
bool m_Alive;
bool m_IsLocal;
CTeamsCore *TeamsCore();
bool Freeze(int Time);
@ -197,6 +198,8 @@ private:
void DDRacePostCoreTick();
void HandleTuneLayer();
CTuningParams *CharacterTuning();
int m_StrongWeakID;
int m_LastWeaponSwitchTick;

View file

@ -64,6 +64,9 @@ void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore
m_pTeams = pTeams;
m_Id = -1;
// fail safe, if core's tuning didn't get updated at all, just fallback to world tuning.
m_Tuning = m_pWorld->m_Tuning[g_Config.m_ClDummy];
Reset();
}
@ -122,11 +125,11 @@ void CCharacterCore::Tick(bool UseInput)
vec2 TargetDirection = normalize(vec2(m_Input.m_TargetX, m_Input.m_TargetY));
m_Vel.y += m_pWorld->m_Tuning[g_Config.m_ClDummy].m_Gravity;
m_Vel.y += m_Tuning.m_Gravity;
float MaxSpeed = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundControlSpeed : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirControlSpeed;
float Accel = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundControlAccel : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirControlAccel;
float Friction = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundFriction : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirFriction;
float MaxSpeed = Grounded ? m_Tuning.m_GroundControlSpeed : m_Tuning.m_AirControlSpeed;
float Accel = Grounded ? m_Tuning.m_GroundControlAccel : m_Tuning.m_AirControlAccel;
float Friction = Grounded ? m_Tuning.m_GroundFriction : m_Tuning.m_AirFriction;
// handle input
if(UseInput)
@ -153,14 +156,14 @@ void CCharacterCore::Tick(bool UseInput)
if(Grounded)
{
m_TriggeredEvents |= COREEVENT_GROUND_JUMP;
m_Vel.y = -m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundJumpImpulse;
m_Vel.y = -m_Tuning.m_GroundJumpImpulse;
m_Jumped |= 1;
m_JumpedTotal = 1;
}
else if(!(m_Jumped & 2))
{
m_TriggeredEvents |= COREEVENT_AIR_JUMP;
m_Vel.y = -m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirJumpImpulse;
m_Vel.y = -m_Tuning.m_AirJumpImpulse;
m_Jumped |= 3;
m_JumpedTotal++;
}
@ -178,7 +181,7 @@ void CCharacterCore::Tick(bool UseInput)
m_HookPos = m_Pos + TargetDirection * PhysSize * 1.5f;
m_HookDir = TargetDirection;
m_HookedPlayer = -1;
m_HookTick = SERVER_TICK_SPEED * (1.25f - m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDuration);
m_HookTick = SERVER_TICK_SPEED * (1.25f - m_Tuning.m_HookDuration);
m_TriggeredEvents |= COREEVENT_HOOK_LAUNCH;
}
}
@ -226,11 +229,11 @@ void CCharacterCore::Tick(bool UseInput)
}
else if(m_HookState == HOOK_FLYING)
{
vec2 NewPos = m_HookPos + m_HookDir * m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed;
if((!m_NewHook && distance(m_Pos, NewPos) > m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength) || (m_NewHook && distance(m_HookTeleBase, NewPos) > m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength))
vec2 NewPos = m_HookPos + m_HookDir * m_Tuning.m_HookFireSpeed;
if((!m_NewHook && distance(m_Pos, NewPos) > m_Tuning.m_HookLength) || (m_NewHook && distance(m_HookTeleBase, NewPos) > m_Tuning.m_HookLength))
{
m_HookState = HOOK_RETRACT_START;
NewPos = m_Pos + normalize(NewPos - m_Pos) * m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength;
NewPos = m_Pos + normalize(NewPos - m_Pos) * m_Tuning.m_HookLength;
m_pReset = true;
}
@ -255,7 +258,7 @@ void CCharacterCore::Tick(bool UseInput)
}
// Check against other players first
if(this->m_Hook && m_pWorld && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerHooking)
if(this->m_Hook && m_pWorld && m_Tuning.m_PlayerHooking)
{
float Distance = 0.0f;
for(int i = 0; i < MAX_CLIENTS; i++)
@ -295,7 +298,7 @@ void CCharacterCore::Tick(bool UseInput)
m_HookState = HOOK_RETRACT_START;
}
if(GoingThroughTele && m_pTeleOuts && m_pTeleOuts->size() && (*m_pTeleOuts)[teleNr - 1].size())
if(GoingThroughTele && m_pWorld && m_pTeleOuts && m_pTeleOuts->size() && (*m_pTeleOuts)[teleNr - 1].size())
{
m_TriggeredEvents = 0;
m_HookedPlayer = -1;
@ -315,7 +318,7 @@ void CCharacterCore::Tick(bool UseInput)
if(m_HookState == HOOK_GRABBED)
{
if(m_HookedPlayer != -1)
if(m_HookedPlayer != -1 && m_pWorld)
{
CCharacterCore *pCharCore = m_pWorld->m_apCharacters[m_HookedPlayer];
if(pCharCore && m_Id != -1 && m_pTeams->CanKeepHook(m_Id, pCharCore->m_Id))
@ -336,7 +339,7 @@ void CCharacterCore::Tick(bool UseInput)
// don't do this hook rutine when we are hook to a player
if(m_HookedPlayer == -1 && distance(m_HookPos, m_Pos) > 46.0f)
{
vec2 HookVel = normalize(m_HookPos - m_Pos) * m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragAccel;
vec2 HookVel = normalize(m_HookPos - m_Pos) * m_Tuning.m_HookDragAccel;
// the hook as more power to drag you up then down.
// this makes it easier to get on top of an platform
if(HookVel.y > 0)
@ -352,13 +355,13 @@ void CCharacterCore::Tick(bool UseInput)
vec2 NewVel = m_Vel + HookVel;
// check if we are under the legal limit for the hook
if(length(NewVel) < m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragSpeed || length(NewVel) < length(m_Vel))
if(length(NewVel) < m_Tuning.m_HookDragSpeed || length(NewVel) < length(m_Vel))
m_Vel = NewVel; // no problem. apply
}
// release hook (max default hook time is 1.25 s)
m_HookTick++;
if(m_HookedPlayer != -1 && (m_HookTick > SERVER_TICK_SPEED + SERVER_TICK_SPEED / 5 || !m_pWorld->m_apCharacters[m_HookedPlayer]))
if(m_HookedPlayer != -1 && (m_HookTick > SERVER_TICK_SPEED + SERVER_TICK_SPEED / 5 || (m_pWorld && !m_pWorld->m_apCharacters[m_HookedPlayer])))
{
m_HookedPlayer = -1;
m_HookState = HOOK_RETRACTED;
@ -389,7 +392,7 @@ void CCharacterCore::Tick(bool UseInput)
{
vec2 Dir = normalize(m_Pos - pCharCore->m_Pos);
bool CanCollide = (m_Super || pCharCore->m_Super) || (pCharCore->m_Collision && m_Collision && !m_NoCollision && !pCharCore->m_NoCollision && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerCollision);
bool CanCollide = (m_Super || pCharCore->m_Super) || (pCharCore->m_Collision && m_Collision && !m_NoCollision && !pCharCore->m_NoCollision && m_Tuning.m_PlayerCollision);
if(CanCollide && Distance < PhysSize * 1.25f && Distance > 0.0f)
{
@ -406,12 +409,12 @@ void CCharacterCore::Tick(bool UseInput)
}
// handle hook influence
if(m_Hook && m_HookedPlayer == i && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerHooking)
if(m_Hook && m_HookedPlayer == i && m_Tuning.m_PlayerHooking)
{
if(Distance > PhysSize * 1.50f) // TODO: fix tweakable variable
{
float Accel = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragAccel * (Distance / m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength);
float DragSpeed = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragSpeed;
float Accel = m_Tuning.m_HookDragAccel * (Distance / m_Tuning.m_HookLength);
float DragSpeed = m_Tuning.m_HookDragSpeed;
vec2 Temp;
// add force to the hooked player
@ -440,7 +443,7 @@ void CCharacterCore::Tick(bool UseInput)
void CCharacterCore::Move()
{
float RampValue = VelocityRamp(length(m_Vel) * 50, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature);
float RampValue = VelocityRamp(length(m_Vel) * 50, m_Tuning.m_VelrampStart, m_Tuning.m_VelrampRange, m_Tuning.m_VelrampCurvature);
m_Vel.x = m_Vel.x * RampValue;
@ -462,7 +465,7 @@ void CCharacterCore::Move()
m_Vel.x = m_Vel.x * (1.0f / RampValue);
if(m_pWorld && (m_Super || (m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerCollision && m_Collision && !m_NoCollision && !m_Solo)))
if(m_pWorld && (m_Super || (m_Tuning.m_PlayerCollision && m_Collision && !m_NoCollision && !m_Solo)))
{
// check player collision
float Distance = distance(m_Pos, NewPos);

View file

@ -265,6 +265,7 @@ public:
bool m_HasTelegunLaser;
int m_FreezeEnd;
bool m_DeepFrozen;
CTuningParams m_Tuning;
private:
CTeamsCore *m_pTeams;

View file

@ -2009,9 +2009,9 @@ void CCharacter::HandleTuneLayer()
m_TuneZone = GameServer()->Collision()->IsTune(CurrentIndex);
if(m_TuneZone)
m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = GameServer()->TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore
m_Core.m_Tuning = GameServer()->TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore
else
m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = *GameServer()->Tuning();
m_Core.m_Tuning = *GameServer()->Tuning();
if(m_TuneZone != m_TuneZoneOld) // don't send tunigs all the time
{