1802: Prediction fixes r=def- a=trml

I believe this fixes #1671 (uninitialized variables).

Some other things: Use new ddnetchar fields in prediction, small fix for prediction of moving pickups, fix the freeze end tick in ddnetcharacter (will add it to the client later)

Also adds a setting for choosing between new/old antiping for grenade like suggested in #1683 (cl_antiping_gunfire 0 to disable).

Co-authored-by: trml <trml@users.noreply.github.com>
This commit is contained in:
bors[bot] 2019-07-04 12:59:52 +00:00
commit b50e7ee674
12 changed files with 224 additions and 154 deletions

View file

@ -112,7 +112,7 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID)
Graphics()->RenderQuadContainerAsSprite(m_ItemsQuadContainerIndex, QuadOffset, Pos.x, Pos.y);
}
void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent)
void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted)
{
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id);
Graphics()->QuadsSetRotation(0);
@ -120,7 +120,8 @@ void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCu
int QuadOffset = 2;
vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), Client()->IntraGameTick());
float IntraTick = IsPredicted ? Client()->PredIntraGameTick() : Client()->IntraGameTick();
vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), IntraTick);
float Angle = 0.0f;
if(pCurrent->m_Type == POWERUP_WEAPON)
{
@ -270,7 +271,7 @@ void CItems::OnRender()
if(Client()->State() < IClient::STATE_ONLINE)
return;
bool UsePredicted = (GameClient()->Predict() && GameClient()->AntiPingWeapons() && GameClient()->AntiPingGrenade());
bool UsePredicted = GameClient()->Predict() && GameClient()->AntiPingGunfire();
if(UsePredicted)
{
for(auto *pProj = (CProjectile*) GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_PROJECTILE); pProj; pProj = (CProjectile*) pProj->NextEntity())
@ -297,7 +298,7 @@ void CItems::OnRender()
CNetObj_Pickup Data, Prev;
pPickup->FillInfo(&Data);
pPrev->FillInfo(&Prev);
RenderPickup(&Prev, &Data);
RenderPickup(&Prev, &Data, true);
}
}
}
@ -439,7 +440,7 @@ void CItems::ReconstructSmokeTrail(const CNetObj_Projectile *pCurrent, int ItemI
if(m_pClient->m_Snap.m_pLocalInfo)
LocalPlayerInGame = m_pClient->m_aClients[m_pClient->m_Snap.m_pLocalInfo->m_ClientID].m_Team != -1;
if(!m_pClient->AntiPingGrenade() || !m_pClient->AntiPingWeapons() || !LocalPlayerInGame)
if(!m_pClient->AntiPingGunfire() || !LocalPlayerInGame)
return;
if(Client()->PredGameTick() == pCurrent->m_StartTick)
return;

View file

@ -15,7 +15,7 @@ class CItems : public CComponent
int m_NumExtraProjectiles;
void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID);
void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent);
void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted = false);
void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData);
void RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted = false);

View file

@ -211,7 +211,7 @@ void CPlayers::RenderPlayer(
bool PredictLocalWeapons = false;
float AttackTime = (Client()->PrevGameTick() - Player.m_AttackTick) / (float)SERVER_TICK_SPEED + Client()->GameTickTime();
float LastAttackTime = (Client()->PrevGameTick() - Player.m_AttackTick) / (float)SERVER_TICK_SPEED + s_LastGameTickTime;
if(Local && m_pClient->m_aClients[ClientID].m_IsPredicted && m_pClient->AntiPingWeapons() && m_pClient->AntiPingGrenade())
if(m_pClient->m_aClients[ClientID].m_IsPredictedLocal && m_pClient->AntiPingGunfire())
{
PredictLocalWeapons = true;
AttackTime = (Client()->PredIntraGameTick() + (Client()->PredGameTick() - 1 - Player.m_AttackTick)) / (float)SERVER_TICK_SPEED;

View file

@ -1209,42 +1209,29 @@ void CGameClient::OnNewSnapshot()
m_Snap.m_aCharacters[Item.m_ID].m_HasExtendedData = true;
// Collision
m_aClients[Item.m_ID].m_Solo = m_aClients[Item.m_ID].m_Predicted.m_Solo =
pCharacterData->m_Flags & CHARACTERFLAG_SOLO;
m_aClients[Item.m_ID].m_NoCollision = m_aClients[Item.m_ID].m_Predicted.m_NoCollision =
pCharacterData->m_Flags & CHARACTERFLAG_NO_COLLISION;
m_aClients[Item.m_ID].m_NoHammerHit = m_aClients[Item.m_ID].m_Predicted.m_NoHammerHit =
pCharacterData->m_Flags & CHARACTERFLAG_NO_HAMMER_HIT;
m_aClients[Item.m_ID].m_NoGrenadeHit = m_aClients[Item.m_ID].m_Predicted.m_NoGrenadeHit =
pCharacterData->m_Flags & CHARACTERFLAG_NO_GRENADE_HIT;
m_aClients[Item.m_ID].m_NoRifleHit = m_aClients[Item.m_ID].m_Predicted.m_NoRifleHit =
pCharacterData->m_Flags & CHARACTERFLAG_NO_RIFLE_HIT;
m_aClients[Item.m_ID].m_NoShotgunHit = m_aClients[Item.m_ID].m_Predicted.m_NoShotgunHit =
pCharacterData->m_Flags & CHARACTERFLAG_NO_SHOTGUN_HIT;
m_aClients[Item.m_ID].m_NoHookHit = m_aClients[Item.m_ID].m_Predicted.m_NoHookHit =
pCharacterData->m_Flags & CHARACTERFLAG_NO_HOOK;
m_aClients[Item.m_ID].m_Super = m_aClients[Item.m_ID].m_Predicted.m_Super =
pCharacterData->m_Flags & CHARACTERFLAG_SUPER;
m_aClients[Item.m_ID].m_Solo = pCharacterData->m_Flags & CHARACTERFLAG_SOLO;
m_aClients[Item.m_ID].m_NoCollision = pCharacterData->m_Flags & CHARACTERFLAG_NO_COLLISION;
m_aClients[Item.m_ID].m_NoHammerHit = pCharacterData->m_Flags & CHARACTERFLAG_NO_HAMMER_HIT;
m_aClients[Item.m_ID].m_NoGrenadeHit = pCharacterData->m_Flags & CHARACTERFLAG_NO_GRENADE_HIT;
m_aClients[Item.m_ID].m_NoRifleHit = pCharacterData->m_Flags & CHARACTERFLAG_NO_RIFLE_HIT;
m_aClients[Item.m_ID].m_NoShotgunHit = pCharacterData->m_Flags & CHARACTERFLAG_NO_SHOTGUN_HIT;
m_aClients[Item.m_ID].m_NoHookHit = pCharacterData->m_Flags & CHARACTERFLAG_NO_HOOK;
m_aClients[Item.m_ID].m_Super = pCharacterData->m_Flags & CHARACTERFLAG_SUPER;
// Endless
m_aClients[Item.m_ID].m_EndlessHook = m_aClients[Item.m_ID].m_Predicted.m_EndlessHook =
pCharacterData->m_Flags & CHARACTERFLAG_ENDLESS_HOOK;
m_aClients[Item.m_ID].m_EndlessJump = m_aClients[Item.m_ID].m_Predicted.m_EndlessJump =
pCharacterData->m_Flags & CHARACTERFLAG_ENDLESS_JUMP;
m_aClients[Item.m_ID].m_EndlessHook = pCharacterData->m_Flags & CHARACTERFLAG_ENDLESS_HOOK;
m_aClients[Item.m_ID].m_EndlessJump = pCharacterData->m_Flags & CHARACTERFLAG_ENDLESS_JUMP;
// Freeze
m_aClients[Item.m_ID].m_FreezeEnd = m_aClients[Item.m_ID].m_Predicted.m_FreezeEnd =
pCharacterData->m_FreezeEnd;
m_aClients[Item.m_ID].m_DeepFrozen = m_aClients[Item.m_ID].m_Predicted.m_DeepFrozen =
pCharacterData->m_FreezeEnd == -1;
m_aClients[Item.m_ID].m_FreezeEnd = pCharacterData->m_FreezeEnd;
m_aClients[Item.m_ID].m_DeepFrozen = pCharacterData->m_FreezeEnd == -1;
// Telegun
m_aClients[Item.m_ID].m_HasTelegunGrenade = m_aClients[Item.m_ID].m_Predicted.m_HasTelegunGrenade =
pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_GRENADE;
m_aClients[Item.m_ID].m_HasTelegunGun = m_aClients[Item.m_ID].m_Predicted.m_HasTelegunGun =
pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_GUN;
m_aClients[Item.m_ID].m_HasTelegunLaser = m_aClients[Item.m_ID].m_Predicted.m_HasTelegunLaser =
pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_LASER;
m_aClients[Item.m_ID].m_HasTelegunGrenade = pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_GRENADE;
m_aClients[Item.m_ID].m_HasTelegunGun = pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_GUN;
m_aClients[Item.m_ID].m_HasTelegunLaser = pCharacterData->m_Flags & CHARACTERFLAG_TELEGUN_LASER;
m_aClients[Item.m_ID].m_Predicted.ReadDDNet(pCharacterData);
}
else if(Item.m_Type == NETOBJTYPE_SPECTATORINFO)
{
@ -2020,13 +2007,33 @@ void CGameClient::UpdatePrediction()
// update strong and weak hook
if(pLocalChar && AntiPingPlayers())
DetectStrongHook();
for(int i : m_CharOrder.m_IDs)
if(CCharacter *pChar = m_GameWorld.GetCharacterByID(i))
{
if(m_Snap.m_aCharacters[m_Snap.m_LocalClientID].m_HasExtendedData)
{
m_GameWorld.RemoveEntity(pChar);
m_GameWorld.InsertEntity(pChar);
int aIDs[MAX_CLIENTS];
for(int i = 0; i < MAX_CLIENTS; i++)
aIDs[i] = -1;
for(int i = 0; i < MAX_CLIENTS; i++)
if(CCharacter *pChar = m_GameWorld.GetCharacterByID(i))
aIDs[pChar->GetStrongWeakID()] = i;
for(int i = 0; i < MAX_CLIENTS; i++)
if(aIDs[i] >= 0)
m_CharOrder.GiveStrong(aIDs[i]);
}
else
{
// manual detection
DetectStrongHook();
}
for(int i : m_CharOrder.m_IDs)
{
if(CCharacter *pChar = m_GameWorld.GetCharacterByID(i))
{
m_GameWorld.RemoveEntity(pChar);
m_GameWorld.InsertEntity(pChar);
}
}
}
// advance the gameworld to the current gametick
if(pLocalChar && abs(m_GameWorld.GameTick() - Client()->GameTick()) < SERVER_TICK_SPEED)
@ -2128,7 +2135,7 @@ void CGameClient::UpdateRenderedCharacters()
{
m_aClients[i].m_IsPredictedLocal = true;
CCharacter *pChar = m_PredictedWorld.GetCharacterByID(i);
if(pChar && AntiPingWeapons() && AntiPingGrenade() && ((pChar->m_NinjaJetpack && pChar->m_FreezeTime == 0) || m_Snap.m_aCharacters[i].m_Cur.m_Weapon != WEAPON_NINJA || m_Snap.m_aCharacters[i].m_Cur.m_Weapon == m_aClients[i].m_Predicted.m_ActiveWeapon))
if(pChar && AntiPingGunfire() && ((pChar->m_NinjaJetpack && pChar->m_FreezeTime == 0) || m_Snap.m_aCharacters[i].m_Cur.m_Weapon != WEAPON_NINJA || m_Snap.m_aCharacters[i].m_Cur.m_Weapon == m_aClients[i].m_Predicted.m_ActiveWeapon))
{
m_aClients[i].m_RenderCur.m_AttackTick = pChar->GetAttackTick();
if(m_Snap.m_aCharacters[i].m_Cur.m_Weapon != WEAPON_NINJA && !(pChar->m_NinjaJetpack && pChar->Core()->m_ActiveWeapon == WEAPON_GUN))
@ -2169,6 +2176,12 @@ void CGameClient::DetectStrongHook()
if(m_Snap.m_aCharacters[FromPlayer].m_Prev.m_Direction != m_Snap.m_aCharacters[FromPlayer].m_Cur.m_Direction
|| m_Snap.m_aCharacters[ToPlayer].m_Prev.m_Direction != m_Snap.m_aCharacters[ToPlayer].m_Cur.m_Direction)
continue;
CCharacter *pFromCharWorld = m_GameWorld.GetCharacterByID(FromPlayer);
CCharacter *pToCharWorld = m_GameWorld.GetCharacterByID(ToPlayer);
if(!pFromCharWorld || !pToCharWorld)
continue;
s_LastUpdateTick[ToPlayer] = s_LastUpdateTick[FromPlayer] = Client()->GameTick();
float PredictErr[2];
@ -2177,14 +2190,15 @@ void CGameClient::DetectStrongHook()
CWorldCore World;
World.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
CCharacterCore ToChar;
CCharacterCore FromChar;
for(int dir = 0; dir < 2; dir++)
{
CCharacterCore ToChar = pFromCharWorld->GetCore();
ToChar.Init(&World, Collision(), &m_Teams);
World.m_apCharacters[ToPlayer] = &ToChar;
ToChar.Read(&m_Snap.m_aCharacters[ToPlayer].m_Prev);
CCharacterCore FromChar = pFromCharWorld->GetCore();
FromChar.Init(&World, Collision(), &m_Teams);
World.m_apCharacters[FromPlayer] = &FromChar;
FromChar.Read(&m_Snap.m_aCharacters[FromPlayer].m_Prev);

View file

@ -440,6 +440,7 @@ public:
bool AntiPingPlayers() { return g_Config.m_ClAntiPing && g_Config.m_ClAntiPingPlayers && !m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK && (m_Tuning[g_Config.m_ClDummy].m_PlayerCollision || m_Tuning[g_Config.m_ClDummy].m_PlayerHooking); }
bool AntiPingGrenade() { return g_Config.m_ClAntiPing && g_Config.m_ClAntiPingGrenade && !m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK; }
bool AntiPingWeapons() { return g_Config.m_ClAntiPing && g_Config.m_ClAntiPingWeapons && !m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK; }
bool AntiPingGunfire() { return AntiPingGrenade() && AntiPingWeapons() && g_Config.m_ClAntiPingGunfire; }
bool Predict() { return g_Config.m_ClPredict && !(m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER) && !m_Snap.m_SpecInfo.m_Active && Client()->State() != IClient::STATE_DEMOPLAYBACK && m_Snap.m_pLocalCharacter; }
CGameWorld m_GameWorld;

View file

@ -1021,6 +1021,8 @@ CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar,
m_Super = false;
m_CanMoveInFreeze = false;
m_Alive = true;
m_TeleCheckpoint = 0;
m_StrongWeakID = 0;
ResetPrediction();
Read(pChar, pExtended, false);
@ -1043,7 +1045,10 @@ void CCharacter::ResetPrediction()
m_DeepFreeze = 0;
m_Super = false;
for(int w = 0; w < NUM_WEAPONS; w++)
{
SetWeaponGot(w, false);
SetWeaponAmmo(w, -1);
}
if(m_Core.m_HookedPlayer >= 0)
{
m_Core.m_HookedPlayer = -1;
@ -1053,126 +1058,22 @@ void CCharacter::ResetPrediction()
void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, bool IsLocal)
{
vec2 PosBefore = m_Pos;
m_Core.Read((CNetObj_CharacterCore*) pChar);
m_Pos = m_Core.m_Pos;
m_AttackTick = pChar->m_AttackTick;
if(distance(PosBefore, m_Pos) > 2.f) // misprediction, don't use prevpos
m_PrevPos = m_Pos;
if(distance(m_PrevPos, m_Pos) > 10.f * 32.f) // reset prevpos if the distance is high
m_PrevPos = m_Pos;
// remove weapons that are unavailable. if the current weapon is ninja just set ammo to zero in case the player is frozen
if(pChar->m_Weapon != m_Core.m_ActiveWeapon)
{
if(pChar->m_Weapon == WEAPON_NINJA)
m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo = 0;
else
{
if(m_Core.m_ActiveWeapon == WEAPON_NINJA)
{
SetNinjaActivationDir(vec2(0,0));
SetNinjaActivationTick(-500);
SetNinjaCurrentMoveTime(0);
}
if(pChar->m_Weapon == m_LastSnapWeapon)
m_aWeapons[m_Core.m_ActiveWeapon].m_Got = false;
}
}
m_LastSnapWeapon = pChar->m_Weapon;
// add weapons
if(pChar->m_Weapon != WEAPON_NINJA)
{
m_aWeapons[pChar->m_Weapon].m_Got = true;
m_aWeapons[pChar->m_Weapon].m_Ammo = (GameWorld()->m_WorldConfig.m_InfiniteAmmo || GameWorld()->m_WorldConfig.m_IsDDRace || pChar->m_Weapon == WEAPON_HAMMER) ? -1 : pChar->m_AmmoCount;
SetActiveWeapon(pChar->m_Weapon);
}
if(pChar->m_Emote != EMOTE_PAIN && pChar->m_Emote != EMOTE_NORMAL)
m_DeepFreeze = false;
if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_FreezeTick || absolute(pChar->m_VelX) == 256*10)
{
m_DeepFreeze = false;
UnFreeze();
}
if(!GameWorld()->m_WorldConfig.m_PredictFreeze)
{
m_DeepFreeze = false;
UnFreeze();
}
if(GameWorld()->m_WorldConfig.m_PredictWeapons && Tuning()->m_JetpackStrength > 0)
{
m_LastJetpackStrength = Tuning()->m_JetpackStrength;
m_Jetpack = true;
m_aWeapons[WEAPON_GUN].m_Got = true;
m_aWeapons[WEAPON_GUN].m_Ammo = -1;
m_NinjaJetpack = pChar->m_Weapon == WEAPON_NINJA;
}
else if(pChar->m_Weapon != WEAPON_NINJA)
m_Jetpack = false;
if(GameWorld()->m_WorldConfig.m_PredictTiles)
{
if(pChar->m_Jumped&2)
{
m_SuperJump = false;
if(m_Core.m_Jumps > m_Core.m_JumpedTotal && m_Core.m_JumpedTotal > 0 && m_Core.m_Jumps > 2)
m_Core.m_Jumps = m_Core.m_JumpedTotal + 1;
else
m_Core.m_JumpedTotal = m_Core.m_Jumps;
}
else
{
if(m_Core.m_Jumps < 2)
m_Core.m_Jumps = m_Core.m_JumpedTotal + 2;
}
if(Tuning()->m_AirJumpImpulse == 0)
{
m_Core.m_Jumps = 0;
m_Core.m_Jumped = 3;
}
}
if(m_Core.m_HookTick != 0)
m_EndlessHook = false;
// reset player collision
if(!pExtended)
SetSolo(!Tuning()->m_PlayerCollision && !Tuning()->m_PlayerHooking);
m_Core.m_Collision = Tuning()->m_PlayerCollision;
m_Core.m_Hook = Tuning()->m_PlayerHooking;
// reset all input except direction and hook for non-local players (as in vanilla prediction)
if(!IsLocal)
{
mem_zero(&m_Input, sizeof(m_Input));
mem_zero(&m_SavedInput, sizeof(m_SavedInput));
m_Input.m_Direction = m_SavedInput.m_Direction = m_Core.m_Direction;
m_Input.m_Hook = m_SavedInput.m_Hook = (m_Core.m_HookState != HOOK_IDLE);
m_Input.m_TargetX = cosf(pChar->m_Angle/256.0f);
m_Input.m_TargetY = sinf(pChar->m_Angle/256.0f);
}
m_Alive = true;
if(pExtended)
{
m_Super = pExtended->m_Flags & CHARACTERFLAG_SUPER;
m_Core.ReadDDNet(pExtended);
SetSolo(pExtended->m_Flags & CHARACTERFLAG_SOLO);
m_Super = pExtended->m_Flags & CHARACTERFLAG_SUPER;
if(m_Super)
TeamsCore()->Team(GetCID(), TeamsCore()->m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER);
m_EndlessHook = pExtended->m_Flags & CHARACTERFLAG_ENDLESS_HOOK;
m_Core.m_Collision = !(pExtended->m_Flags & CHARACTERFLAG_NO_COLLISION);
m_Core.m_Hook = !(pExtended->m_Flags & CHARACTERFLAG_NO_HOOK);
m_SuperJump = pExtended->m_Flags & CHARACTERFLAG_ENDLESS_JUMP;
m_Jetpack = pExtended->m_Flags & CHARACTERFLAG_JETPACK;
m_TeleCheckpoint = pExtended->m_TeleCheckpoint;
m_StrongWeakID = pExtended->m_StrongWeakID;
m_Hit = HIT_ALL;
if(pExtended->m_Flags & CHARACTERFLAG_NO_GRENADE_HIT)
@ -1183,6 +1084,123 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
m_Hit |= DISABLE_HIT_RIFLE;
if(pExtended->m_Flags & CHARACTERFLAG_NO_SHOTGUN_HIT)
m_Hit |= DISABLE_HIT_SHOTGUN;
m_aWeapons[WEAPON_HAMMER].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_HAMMER) != 0;
m_aWeapons[WEAPON_GUN].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_GUN) != 0;
m_aWeapons[WEAPON_SHOTGUN].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_SHOTGUN) != 0;
m_aWeapons[WEAPON_GRENADE].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_GRENADE) != 0;
m_aWeapons[WEAPON_RIFLE].m_Got = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_LASER) != 0;
const bool Ninja = (pExtended->m_Flags & CHARACTERFLAG_WEAPON_NINJA) != 0;
if(Ninja && m_Core.m_ActiveWeapon != WEAPON_NINJA)
GiveNinja();
else if(!Ninja && m_Core.m_ActiveWeapon == WEAPON_NINJA)
RemoveNinja();
}
else
{
// ddnetcharacter is not available, try to get some info from the tunings and the character netobject instead.
// remove weapons that are unavailable. if the current weapon is ninja just set ammo to zero in case the player is frozen
if(pChar->m_Weapon != m_Core.m_ActiveWeapon)
{
if(pChar->m_Weapon == WEAPON_NINJA)
m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo = 0;
else
{
if(m_Core.m_ActiveWeapon == WEAPON_NINJA)
{
SetNinjaActivationDir(vec2(0,0));
SetNinjaActivationTick(-500);
SetNinjaCurrentMoveTime(0);
}
if(pChar->m_Weapon == m_LastSnapWeapon)
m_aWeapons[m_Core.m_ActiveWeapon].m_Got = false;
}
}
// add weapon
if(pChar->m_Weapon != WEAPON_NINJA)
m_aWeapons[pChar->m_Weapon].m_Got = true;
// jetpack
if(GameWorld()->m_WorldConfig.m_PredictWeapons && Tuning()->m_JetpackStrength > 0)
{
m_LastJetpackStrength = Tuning()->m_JetpackStrength;
m_Jetpack = true;
m_aWeapons[WEAPON_GUN].m_Got = true;
m_aWeapons[WEAPON_GUN].m_Ammo = -1;
m_NinjaJetpack = pChar->m_Weapon == WEAPON_NINJA;
}
else if(pChar->m_Weapon != WEAPON_NINJA)
m_Jetpack = false;
// number of jumps
if(GameWorld()->m_WorldConfig.m_PredictTiles)
{
if(pChar->m_Jumped&2)
{
m_SuperJump = false;
if(m_Core.m_Jumps > m_Core.m_JumpedTotal && m_Core.m_JumpedTotal > 0 && m_Core.m_Jumps > 2)
m_Core.m_Jumps = m_Core.m_JumpedTotal + 1;
}
else if(m_Core.m_Jumps < 2)
m_Core.m_Jumps = m_Core.m_JumpedTotal + 2;
if(Tuning()->m_AirJumpImpulse == 0)
{
m_Core.m_Jumps = 0;
m_Core.m_Jumped = 3;
}
}
// set player collision
SetSolo(!Tuning()->m_PlayerCollision && !Tuning()->m_PlayerHooking);
m_Core.m_Collision = Tuning()->m_PlayerCollision;
m_Core.m_Hook = Tuning()->m_PlayerHooking;
if(m_Core.m_HookTick != 0)
m_EndlessHook = false;
}
// detect unfreeze (in case the player was frozen in the tile prediction and not correclty unfrozen)
if(pChar->m_Emote != EMOTE_PAIN && pChar->m_Emote != EMOTE_NORMAL)
m_DeepFreeze = false;
if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_FreezeTick || absolute(pChar->m_VelX) == 256*10 || !GameWorld()->m_WorldConfig.m_PredictFreeze)
{
m_DeepFreeze = false;
UnFreeze();
}
vec2 PosBefore = m_Pos;
m_Pos = m_Core.m_Pos;
if(distance(PosBefore, m_Pos) > 2.f) // misprediction, don't use prevpos
m_PrevPos = m_Pos;
if(distance(m_PrevPos, m_Pos) > 10.f * 32.f) // reset prevpos if the distance is high
m_PrevPos = m_Pos;
if(pChar->m_Jumped&2)
m_Core.m_JumpedTotal = m_Core.m_Jumps;
m_AttackTick = pChar->m_AttackTick;
m_LastSnapWeapon = pChar->m_Weapon;
m_Alive = true;
// set the current weapon
if(pChar->m_Weapon != WEAPON_NINJA)
{
m_aWeapons[pChar->m_Weapon].m_Ammo = (GameWorld()->m_WorldConfig.m_InfiniteAmmo || GameWorld()->m_WorldConfig.m_IsDDRace || pChar->m_Weapon == WEAPON_HAMMER) ? -1 : pChar->m_AmmoCount;
SetActiveWeapon(pChar->m_Weapon);
}
// reset all input except direction and hook for non-local players (as in vanilla prediction)
if(!IsLocal)
{
mem_zero(&m_Input, sizeof(m_Input));
mem_zero(&m_SavedInput, sizeof(m_SavedInput));
m_Input.m_Direction = m_SavedInput.m_Direction = m_Core.m_Direction;
m_Input.m_Hook = m_SavedInput.m_Hook = (m_Core.m_HookState != HOOK_IDLE);
m_Input.m_TargetX = cosf(pChar->m_Angle/256.0f);
m_Input.m_TargetY = sinf(pChar->m_Angle/256.0f);
}
}

View file

@ -90,6 +90,7 @@ public:
int m_Hit;
vec2 m_PrevPos;
vec2 m_PrevPrevPos;
int m_TeleCheckpoint;
int m_TileIndex;
int m_TileFlags;
@ -142,6 +143,7 @@ public:
void SetInput(CNetObj_PlayerInput *pNewInput) { m_LatestInput = m_Input = *pNewInput; };
int GetJumped() { return m_Core.m_Jumped; }
int GetAttackTick() { return m_AttackTick; }
int GetStrongWeakID() { return m_StrongWeakID; }
CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended = 0);
void Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended, bool IsLocal);
@ -205,6 +207,8 @@ private:
void HandleSkippableTiles(int Index);
void DDRaceTick();
void DDRacePostCoreTick();
int m_StrongWeakID;
};
enum

View file

@ -93,6 +93,8 @@ CPickup::CPickup(CGameWorld *pGameWorld, int ID, CNetObj_Pickup *pPickup)
m_Subtype = pPickup->m_Subtype;
m_Core = vec2(0.f, 0.f);
m_ID = ID;
m_Layer = LAYER_GAME;
m_Number = 0;
}
void CPickup::FillInfo(CNetObj_Pickup *pPickup)

View file

@ -606,6 +606,34 @@ void CCharacterCore::Read(const CNetObj_CharacterCore *pObjCore)
m_Angle = pObjCore->m_Angle;
}
void CCharacterCore::ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet)
{
// Collision
m_Solo = pObjDDNet->m_Flags & CHARACTERFLAG_SOLO;
m_NoCollision = pObjDDNet->m_Flags & CHARACTERFLAG_NO_COLLISION;
m_NoHammerHit = pObjDDNet->m_Flags & CHARACTERFLAG_NO_HAMMER_HIT;
m_NoGrenadeHit = pObjDDNet->m_Flags & CHARACTERFLAG_NO_GRENADE_HIT;
m_NoRifleHit = pObjDDNet->m_Flags & CHARACTERFLAG_NO_RIFLE_HIT;
m_NoShotgunHit = pObjDDNet->m_Flags & CHARACTERFLAG_NO_SHOTGUN_HIT;
m_NoHookHit = pObjDDNet->m_Flags & CHARACTERFLAG_NO_HOOK;
m_Super = pObjDDNet->m_Flags & CHARACTERFLAG_SUPER;
// Endless
m_EndlessHook = pObjDDNet->m_Flags & CHARACTERFLAG_ENDLESS_HOOK;
m_EndlessJump = pObjDDNet->m_Flags & CHARACTERFLAG_ENDLESS_JUMP;
// Freeze
m_FreezeEnd = pObjDDNet->m_FreezeEnd;
m_DeepFrozen = pObjDDNet->m_FreezeEnd == -1;
// Telegun
m_HasTelegunGrenade = pObjDDNet->m_Flags & CHARACTERFLAG_TELEGUN_GRENADE;
m_HasTelegunGun = pObjDDNet->m_Flags & CHARACTERFLAG_TELEGUN_GUN;
m_HasTelegunLaser = pObjDDNet->m_Flags & CHARACTERFLAG_TELEGUN_LASER;
m_Jumps = pObjDDNet->m_Jumps;
}
void CCharacterCore::Quantize()
{
CNetObj_CharacterCore Core;

View file

@ -232,6 +232,7 @@ public:
bool m_LeftWall;
// DDnet Character
void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet);
bool m_Solo;
bool m_Jetpack;
bool m_NoCollision;

View file

@ -1213,7 +1213,7 @@ void CCharacter::Snap(int SnappingClient)
if(m_Core.m_ActiveWeapon == WEAPON_NINJA)
pDDNetCharacter->m_Flags |= CHARACTERFLAG_WEAPON_NINJA;
pDDNetCharacter->m_FreezeEnd = m_DeepFreeze ? -1 : m_FreezeTick + m_FreezeTime;
pDDNetCharacter->m_FreezeEnd = m_DeepFreeze ? -1 : m_FreezeTime == 0 ? 0 : Server()->Tick() + m_FreezeTime;
pDDNetCharacter->m_Jumps = m_Core.m_Jumps;
pDDNetCharacter->m_TeleCheckpoint = m_TeleCheckpoint;
pDDNetCharacter->m_StrongWeakID = m_StrongWeakID;

View file

@ -12,6 +12,7 @@ MACRO_CONFIG_INT(ClAntiPingPlayers, cl_antiping_players, 1, 0, 1, CFGFLAG_CLIENT
MACRO_CONFIG_INT(ClAntiPingGrenade, cl_antiping_grenade, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict grenades (only enabled if cl_antiping is set to 1)")
MACRO_CONFIG_INT(ClAntiPingWeapons, cl_antiping_weapons, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict weapon projectiles (only enabled if cl_antiping is set to 1)")
MACRO_CONFIG_INT(ClAntiPingSmooth, cl_antiping_smooth, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Make the prediction of other player's movement smoother")
MACRO_CONFIG_INT(ClAntiPingGunfire, cl_antiping_gunfire, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict gunfire and show predicted weapon physics (with cl_antiping_grenade 1 and cl_antiping_weapons 1)")
MACRO_CONFIG_INT(ClNameplates, cl_nameplates, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show name plates")
MACRO_CONFIG_INT(ClNameplatesAlways, cl_nameplates_always, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Always show name plates disregarding of distance")