This commit is contained in:
Tater 2024-09-18 14:22:19 +03:00 committed by GitHub
commit 21584980bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 124 additions and 113 deletions

View file

@ -333,9 +333,8 @@ public:
virtual void OnClientEnter(int ClientId) = 0; virtual void OnClientEnter(int ClientId) = 0;
virtual void OnClientDrop(int ClientId, const char *pReason) = 0; virtual void OnClientDrop(int ClientId, const char *pReason) = 0;
virtual void OnClientPrepareInput(int ClientId, void *pInput) = 0; virtual void OnClientPrepareInput(int ClientId, void *pInput) = 0;
virtual void OnClientDirectInput(int ClientId, void *pInput) = 0; virtual void OnClientFreshInput(int ClientId, void *pInput) = 0;
virtual void OnClientPredictedInput(int ClientId, void *pInput) = 0; virtual void OnClientPredictedInput(int ClientId, void *pInput) = 0;
virtual void OnClientPredictedEarlyInput(int ClientId, void *pInput) = 0;
virtual bool IsClientReady(int ClientId) const = 0; virtual bool IsClientReady(int ClientId) const = 0;
virtual bool IsClientPlayer(int ClientId) const = 0; virtual bool IsClientPlayer(int ClientId) const = 0;

View file

@ -208,8 +208,6 @@ void CServer::CClient::Reset()
// reset input // reset input
for(auto &Input : m_aInputs) for(auto &Input : m_aInputs)
Input.m_GameTick = -1; Input.m_GameTick = -1;
m_CurrentInput = 0;
mem_zero(&m_LatestInput, sizeof(m_LatestInput));
m_Snapshots.PurgeAll(); m_Snapshots.PurgeAll();
m_LastAckedSnapshot = -1; m_LastAckedSnapshot = -1;
@ -1633,12 +1631,22 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
const int LastAckedSnapshot = Unpacker.GetInt(); const int LastAckedSnapshot = Unpacker.GetInt();
int IntendedTick = Unpacker.GetInt(); int IntendedTick = Unpacker.GetInt();
int Size = Unpacker.GetInt(); int Size = Unpacker.GetInt();
int BufferPosition = IntendedTick % 200;
// The client is not allowed to change inputs for a tick they have already sent to the server
if(m_aClients[ClientId].m_aInputs[BufferPosition].m_GameTick == IntendedTick)
{
return;
}
if(Unpacker.Error() || Size / 4 > MAX_INPUT_SIZE || IntendedTick < MIN_TICK || IntendedTick >= MAX_TICK) if(Unpacker.Error() || Size / 4 > MAX_INPUT_SIZE || IntendedTick < MIN_TICK || IntendedTick >= MAX_TICK)
{ {
return; return;
} }
m_aClients[ClientId].m_LastAckedSnapshot = LastAckedSnapshot; // The LastAckedSnapshot should only increase
if(LastAckedSnapshot > m_aClients[ClientId].m_LastAckedSnapshot || LastAckedSnapshot == -1)
m_aClients[ClientId].m_LastAckedSnapshot = LastAckedSnapshot;
if(m_aClients[ClientId].m_LastAckedSnapshot > 0) if(m_aClients[ClientId].m_LastAckedSnapshot > 0)
m_aClients[ClientId].m_SnapRate = CClient::SNAPRATE_FULL; m_aClients[ClientId].m_SnapRate = CClient::SNAPRATE_FULL;
@ -1660,11 +1668,18 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
m_aClients[ClientId].m_LastInputTick = IntendedTick; m_aClients[ClientId].m_LastInputTick = IntendedTick;
CClient::CInput *pInput = &m_aClients[ClientId].m_aInputs[m_aClients[ClientId].m_CurrentInput]; // TODO: This should probably not be here, the most recent input can be found by looping over the ring buffer
// so we should not change the inputs original intended tick since we might need that information
if(IntendedTick <= Tick()) if(IntendedTick <= Tick())
IntendedTick = Tick() + 1; IntendedTick = Tick() + 1;
// Check once again that we are not overriding an input the client has already sent
// This is a workaround while the above code is still able to change IntendedTick
BufferPosition = IntendedTick % 200;
if(m_aClients[ClientId].m_aInputs[BufferPosition].m_GameTick == IntendedTick)
return;
CClient::CInput *pInput = &m_aClients[ClientId].m_aInputs[BufferPosition];
pInput->m_GameTick = IntendedTick; pInput->m_GameTick = IntendedTick;
for(int i = 0; i < Size / 4; i++) for(int i = 0; i < Size / 4; i++)
@ -1677,14 +1692,13 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
} }
GameServer()->OnClientPrepareInput(ClientId, pInput->m_aData); GameServer()->OnClientPrepareInput(ClientId, pInput->m_aData);
mem_copy(m_aClients[ClientId].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE * sizeof(int));
m_aClients[ClientId].m_CurrentInput++; CClient::CInput LatestInput;
m_aClients[ClientId].m_CurrentInput %= 200; mem_copy(LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE * sizeof(int));
// call the mod with the fresh input data // call the mod with the fresh input data
if(m_aClients[ClientId].m_State == CClient::STATE_INGAME) if(m_aClients[ClientId].m_State == CClient::STATE_INGAME)
GameServer()->OnClientDirectInput(ClientId, m_aClients[ClientId].m_LatestInput.m_aData); GameServer()->OnClientFreshInput(ClientId, LatestInput.m_aData);
} }
else if(Msg == NETMSG_RCON_CMD) else if(Msg == NETMSG_RCON_CMD)
{ {
@ -2666,8 +2680,6 @@ void CServer::UpdateDebugDummies(bool ForceDisconnect)
Input.m_Direction = (ClientId & 1) ? -1 : 1; Input.m_Direction = (ClientId & 1) ? -1 : 1;
m_aClients[ClientId].m_aInputs[0].m_GameTick = Tick() + 1; m_aClients[ClientId].m_aInputs[0].m_GameTick = Tick() + 1;
mem_copy(m_aClients[ClientId].m_aInputs[0].m_aData, &Input, minimum(sizeof(Input), sizeof(m_aClients[ClientId].m_aInputs[0].m_aData))); mem_copy(m_aClients[ClientId].m_aInputs[0].m_aData, &Input, minimum(sizeof(Input), sizeof(m_aClients[ClientId].m_aInputs[0].m_aData)));
m_aClients[ClientId].m_LatestInput = m_aClients[ClientId].m_aInputs[0];
m_aClients[ClientId].m_CurrentInput = 0;
} }
} }
@ -2888,23 +2900,6 @@ int CServer::Run()
UpdateDebugDummies(false); UpdateDebugDummies(false);
#endif #endif
for(int c = 0; c < MAX_CLIENTS; c++)
{
if(m_aClients[c].m_State != CClient::STATE_INGAME)
continue;
bool ClientHadInput = false;
for(auto &Input : m_aClients[c].m_aInputs)
{
if(Input.m_GameTick == Tick() + 1)
{
GameServer()->OnClientPredictedEarlyInput(c, Input.m_aData);
ClientHadInput = true;
}
}
if(!ClientHadInput)
GameServer()->OnClientPredictedEarlyInput(c, nullptr);
}
m_CurrentGameTick++; m_CurrentGameTick++;
NewTicks++; NewTicks++;
@ -2926,7 +2921,6 @@ int CServer::Run()
if(!ClientHadInput) if(!ClientHadInput)
GameServer()->OnClientPredictedInput(c, nullptr); GameServer()->OnClientPredictedInput(c, nullptr);
} }
GameServer()->OnTick(); GameServer()->OnTick();
if(ErrorShutdown()) if(ErrorShutdown())
{ {

View file

@ -147,9 +147,7 @@ public:
int m_LastInputTick; int m_LastInputTick;
CSnapshotStorage m_Snapshots; CSnapshotStorage m_Snapshots;
CInput m_LatestInput; CInput m_aInputs[200];
CInput m_aInputs[200]; // TODO: handle input better
int m_CurrentInput;
char m_aName[MAX_NAME_LENGTH]; char m_aName[MAX_NAME_LENGTH];
char m_aClan[MAX_CLAN_LENGTH]; char m_aClan[MAX_CLAN_LENGTH];

View file

@ -403,7 +403,7 @@ void CCharacter::HandleWeaponSwitch()
DoWeaponSwitch(); DoWeaponSwitch();
} }
void CCharacter::FireWeapon() void CCharacter::FireWeapon(bool EarlyFire)
{ {
if(m_ReloadTimer != 0) if(m_ReloadTimer != 0)
{ {
@ -530,7 +530,7 @@ void CCharacter::FireWeapon()
{ {
int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GunLifetime); int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GunLifetime);
new CProjectile( CProjectile *Projectile = new CProjectile(
GameWorld(), GameWorld(),
WEAPON_GUN, //Type WEAPON_GUN, //Type
m_pPlayer->GetCid(), //Owner m_pPlayer->GetCid(), //Owner
@ -542,6 +542,8 @@ void CCharacter::FireWeapon()
-1, //SoundImpact -1, //SoundImpact
MouseTarget //InitDir MouseTarget //InitDir
); );
if(EarlyFire)
Projectile->SetStartTick(Server()->Tick() - 1);
GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
} }
@ -552,7 +554,10 @@ void CCharacter::FireWeapon()
{ {
float LaserReach = GetTuning(m_TuneZone)->m_LaserReach; float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;
new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_SHOTGUN); CLaser *Laser = new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_SHOTGUN);
if(EarlyFire)
Laser->SetEvalTick(Server()->Tick() - 1);
GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
} }
break; break;
@ -561,7 +566,7 @@ void CCharacter::FireWeapon()
{ {
int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GrenadeLifetime); int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GrenadeLifetime);
new CProjectile( CProjectile *Projectile = new CProjectile(
GameWorld(), GameWorld(),
WEAPON_GRENADE, //Type WEAPON_GRENADE, //Type
m_pPlayer->GetCid(), //Owner m_pPlayer->GetCid(), //Owner
@ -573,6 +578,8 @@ void CCharacter::FireWeapon()
SOUND_GRENADE_EXPLODE, //SoundImpact SOUND_GRENADE_EXPLODE, //SoundImpact
MouseTarget // MouseTarget MouseTarget // MouseTarget
); );
if(EarlyFire)
Projectile->SetStartTick(Server()->Tick() - 1);
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
} }
@ -582,7 +589,10 @@ void CCharacter::FireWeapon()
{ {
float LaserReach = GetTuning(m_TuneZone)->m_LaserReach; float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;
new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_LASER); CLaser *Laser = new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_LASER);
if(EarlyFire)
Laser->SetEvalTick(Server()->Tick() - 1);
GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
} }
break; break;
@ -602,6 +612,8 @@ void CCharacter::FireWeapon()
} }
m_AttackTick = Server()->Tick(); m_AttackTick = Server()->Tick();
if(EarlyFire)
m_AttackTick--;
if(!m_ReloadTimer) if(!m_ReloadTimer)
{ {
@ -671,6 +683,7 @@ void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput)
// copy new input // copy new input
mem_copy(&m_Input, pNewInput, sizeof(m_Input)); mem_copy(&m_Input, pNewInput, sizeof(m_Input));
m_NumInputs++;
// it is not allowed to aim in the center // it is not allowed to aim in the center
if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0)
@ -679,28 +692,6 @@ void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput)
mem_copy(&m_SavedInput, &m_Input, sizeof(m_SavedInput)); mem_copy(&m_SavedInput, &m_Input, sizeof(m_SavedInput));
} }
void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
{
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput));
m_NumInputs++;
// it is not allowed to aim in the center
if(m_LatestInput.m_TargetX == 0 && m_LatestInput.m_TargetY == 0)
m_LatestInput.m_TargetY = -1;
Antibot()->OnDirectInput(m_pPlayer->GetCid());
if(m_NumInputs > 1 && m_pPlayer->GetTeam() != TEAM_SPECTATORS)
{
HandleWeaponSwitch();
FireWeapon();
}
mem_copy(&m_LatestPrevPrevInput, &m_LatestPrevInput, sizeof(m_LatestInput));
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
}
void CCharacter::ReleaseHook() void CCharacter::ReleaseHook()
{ {
m_Core.SetHookedPlayer(-1); m_Core.SetHookedPlayer(-1);
@ -725,6 +716,27 @@ void CCharacter::ResetInput()
m_LatestPrevInput = m_LatestInput = m_Input; m_LatestPrevInput = m_LatestInput = m_Input;
} }
void CCharacter::WeaponTick()
{
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
mem_copy(&m_LatestInput, &m_Input, sizeof(m_LatestInput));
// it is not allowed to aim in the center
if(m_LatestInput.m_TargetX == 0 && m_LatestInput.m_TargetY == 0)
m_LatestInput.m_TargetY = -1;
Antibot()->OnDirectInput(m_pPlayer->GetCid());
if(m_NumInputs > 1 && m_pPlayer->GetTeam() != TEAM_SPECTATORS)
{
HandleWeaponSwitch();
FireWeapon(true);
}
mem_copy(&m_LatestPrevPrevInput, &m_LatestPrevInput, sizeof(m_LatestInput));
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
}
void CCharacter::PreTick() void CCharacter::PreTick()
{ {
if(m_StartTime > Server()->Tick()) if(m_StartTime > Server()->Tick())

View file

@ -34,6 +34,7 @@ public:
void Reset() override; void Reset() override;
void Destroy() override; void Destroy() override;
void PreTick(); void PreTick();
void WeaponTick();
void Tick() override; void Tick() override;
void TickDeferred() override; void TickDeferred() override;
void TickPaused() override; void TickPaused() override;
@ -61,11 +62,11 @@ public:
void HandleJetpack(); void HandleJetpack();
void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
void OnDirectInput(CNetObj_PlayerInput *pNewInput);
void ReleaseHook(); void ReleaseHook();
void ResetHook(); void ResetHook();
void ResetInput(); void ResetInput();
void FireWeapon(); void FireWeapon(bool EarlyFire = false);
void Die(int Killer, int Weapon, bool SendKillMsg = true); void Die(int Killer, int Weapon, bool SendKillMsg = true);
bool TakeDamage(vec2 Force, int Dmg, int From, int Weapon); bool TakeDamage(vec2 Force, int Dmg, int From, int Weapon);

View file

@ -35,6 +35,11 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner
DoBounce(); DoBounce();
} }
void CLaser::SetEvalTick(int Tick)
{
m_EvalTick = Tick;
}
bool CLaser::HitCharacter(vec2 From, vec2 To) bool CLaser::HitCharacter(vec2 From, vec2 To)
{ {
static const vec2 StackedLaserShotgunBugSpeed = vec2(-2147483648.0f, -2147483648.0f); static const vec2 StackedLaserShotgunBugSpeed = vec2(-2147483648.0f, -2147483648.0f);
@ -98,9 +103,11 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
return true; return true;
} }
void CLaser::DoBounce() void CLaser::DoBounce(bool EarlyTick)
{ {
m_EvalTick = Server()->Tick(); m_EvalTick = Server()->Tick();
if(EarlyTick)
m_EvalTick--;
if(m_Energy < 0) if(m_Energy < 0)
{ {

View file

@ -8,7 +8,13 @@
class CLaser : public CEntity class CLaser : public CEntity
{ {
public: public:
CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner, int Type); CLaser(
CGameWorld *pGameWorld,
vec2 Pos,
vec2 Direction,
float StartEnergy,
int Owner,
int Type);
virtual void Reset() override; virtual void Reset() override;
virtual void Tick() override; virtual void Tick() override;
@ -20,7 +26,7 @@ public:
protected: protected:
bool HitCharacter(vec2 From, vec2 To); bool HitCharacter(vec2 From, vec2 To);
void DoBounce(); void DoBounce(bool EarlyTick = false);
private: private:
vec2 m_From; vec2 m_From;
@ -42,6 +48,9 @@ private:
bool m_TeleportCancelled; bool m_TeleportCancelled;
bool m_IsBlueTeleport; bool m_IsBlueTeleport;
bool m_BelongsToPracticeTeam; bool m_BelongsToPracticeTeam;
public:
void SetEvalTick(int Tick);
}; };
#endif #endif

View file

@ -49,6 +49,11 @@ CProjectile::CProjectile(
GameWorld()->InsertEntity(this); GameWorld()->InsertEntity(this);
} }
void CProjectile::SetStartTick(int Tick)
{
m_StartTick = Tick;
}
void CProjectile::Reset() void CProjectile::Reset()
{ {
m_MarkedForDestroy = true; m_MarkedForDestroy = true;

View file

@ -50,6 +50,7 @@ private:
vec2 m_InitDir; vec2 m_InitDir;
public: public:
void SetStartTick(int Tick);
void SetBouncing(int Value); void SetBouncing(int Value);
bool FillExtraInfoLegacy(CNetObj_DDRaceProjectile *pProj); bool FillExtraInfoLegacy(CNetObj_DDRaceProjectile *pProj);
void FillExtraInfo(CNetObj_DDNetProjectile *pProj); void FillExtraInfo(CNetObj_DDNetProjectile *pProj);

View file

@ -1314,6 +1314,8 @@ void CGameContext::OnTick()
} }
// Server hooks // Server hooks
// Called on all incoming NETMSG_INPUT, reformats player flags for sixup compatibility
void CGameContext::OnClientPrepareInput(int ClientId, void *pInput) void CGameContext::OnClientPrepareInput(int ClientId, void *pInput)
{ {
auto *pPlayerInput = (CNetObj_PlayerInput *)pInput; auto *pPlayerInput = (CNetObj_PlayerInput *)pInput;
@ -1321,10 +1323,11 @@ void CGameContext::OnClientPrepareInput(int ClientId, void *pInput)
pPlayerInput->m_PlayerFlags = PlayerFlags_SevenToSix(pPlayerInput->m_PlayerFlags); pPlayerInput->m_PlayerFlags = PlayerFlags_SevenToSix(pPlayerInput->m_PlayerFlags);
} }
void CGameContext::OnClientDirectInput(int ClientId, void *pInput) // Called on all incoming NETMSG_INPUT, only sets player flags and tracks afk status
void CGameContext::OnClientFreshInput(int ClientId, void *pInput)
{ {
if(!m_World.m_Paused) if(!m_World.m_Paused)
m_apPlayers[ClientId]->OnDirectInput((CNetObj_PlayerInput *)pInput); m_apPlayers[ClientId]->OnPlayerFreshInput((CNetObj_PlayerInput *)pInput);
int Flags = ((CNetObj_PlayerInput *)pInput)->m_PlayerFlags; int Flags = ((CNetObj_PlayerInput *)pInput)->m_PlayerFlags;
if((Flags & 256) || (Flags & 512)) if((Flags & 256) || (Flags & 512))
@ -1333,26 +1336,11 @@ void CGameContext::OnClientDirectInput(int ClientId, void *pInput)
} }
} }
// Called once per input that happens on this tick.
// pInput is nullptr if the client did not send any fresh input this tick.
void CGameContext::OnClientPredictedInput(int ClientId, void *pInput) void CGameContext::OnClientPredictedInput(int ClientId, void *pInput)
{ {
// early return if no input at all has been sent by a player // early return if no input has ever been sent by the player
if(pInput == nullptr && !m_aPlayerHasInput[ClientId])
return;
// set to last sent input when no new input has been sent
CNetObj_PlayerInput *pApplyInput = (CNetObj_PlayerInput *)pInput;
if(pApplyInput == nullptr)
{
pApplyInput = &m_aLastPlayerInput[ClientId];
}
if(!m_World.m_Paused)
m_apPlayers[ClientId]->OnPredictedInput(pApplyInput);
}
void CGameContext::OnClientPredictedEarlyInput(int ClientId, void *pInput)
{
// early return if no input at all has been sent by a player
if(pInput == nullptr && !m_aPlayerHasInput[ClientId]) if(pInput == nullptr && !m_aPlayerHasInput[ClientId])
return; return;
@ -1364,16 +1352,12 @@ void CGameContext::OnClientPredictedEarlyInput(int ClientId, void *pInput)
} }
else else
{ {
// Store input in this function and not in `OnClientPredictedInput`,
// because this function is called on all inputs, while
// `OnClientPredictedInput` is only called on the first input of each
// tick.
mem_copy(&m_aLastPlayerInput[ClientId], pApplyInput, sizeof(m_aLastPlayerInput[ClientId])); mem_copy(&m_aLastPlayerInput[ClientId], pApplyInput, sizeof(m_aLastPlayerInput[ClientId]));
m_aPlayerHasInput[ClientId] = true; m_aPlayerHasInput[ClientId] = true;
} }
if(!m_World.m_Paused) if(!m_World.m_Paused)
m_apPlayers[ClientId]->OnPredictedEarlyInput(pApplyInput); m_apPlayers[ClientId]->OnPlayerInput(pApplyInput);
if(m_TeeHistorianActive) if(m_TeeHistorianActive)
{ {

View file

@ -320,9 +320,8 @@ public:
void OnClientEnter(int ClientId) override; void OnClientEnter(int ClientId) override;
void OnClientDrop(int ClientId, const char *pReason) override; void OnClientDrop(int ClientId, const char *pReason) override;
void OnClientPrepareInput(int ClientId, void *pInput) override; void OnClientPrepareInput(int ClientId, void *pInput) override;
void OnClientDirectInput(int ClientId, void *pInput) override; void OnClientFreshInput(int ClientId, void *pInput) override;
void OnClientPredictedInput(int ClientId, void *pInput) override; void OnClientPredictedInput(int ClientId, void *pInput) override;
void OnClientPredictedEarlyInput(int ClientId, void *pInput) override;
void TeehistorianRecordAntibot(const void *pData, int DataSize) override; void TeehistorianRecordAntibot(const void *pData, int DataSize) override;
void TeehistorianRecordPlayerJoin(int ClientId, bool Sixup) override; void TeehistorianRecordPlayerJoin(int ClientId, bool Sixup) override;

View file

@ -6,6 +6,7 @@
#include "entity.h" #include "entity.h"
#include "gamecontext.h" #include "gamecontext.h"
#include "gamecontroller.h" #include "gamecontroller.h"
#include "player.h"
#include <engine/shared/config.h> #include <engine/shared/config.h>
@ -214,6 +215,17 @@ void CGameWorld::Tick()
if(GameServer()->m_pController->IsForceBalanced()) if(GameServer()->m_pController->IsForceBalanced())
GameServer()->SendChat(-1, TEAM_ALL, "Teams have been balanced"); GameServer()->SendChat(-1, TEAM_ALL, "Teams have been balanced");
// This is placed here so that certain weapon physics can happen before the regular Charecter Tick() to preserve physics accuracy.
// It is done in client order to preserve previous behavior.
for(auto &pPlayer : GameServer()->m_apPlayers)
{
if(!pPlayer)
continue;
CCharacter *pChar = pPlayer->GetCharacter();
if(pChar)
pChar->WeaponTick();
}
// update all objects // update all objects
for(int i = 0; i < NUM_ENTTYPES; i++) for(int i = 0; i < NUM_ENTTYPES; i++)
{ {

View file

@ -500,8 +500,13 @@ void CPlayer::OnDisconnect()
m_Moderating = false; m_Moderating = false;
} }
void CPlayer::OnPredictedInput(CNetObj_PlayerInput *pNewInput) void CPlayer::OnPlayerInput(CNetObj_PlayerInput *pNewInput)
{ {
m_PlayerFlags = pNewInput->m_PlayerFlags;
if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (pNewInput->m_Fire & 1))
m_Spawning = true;
// skip the input if chat is active // skip the input if chat is active
if((m_PlayerFlags & PLAYERFLAG_CHATTING) && (pNewInput->m_PlayerFlags & PLAYERFLAG_CHATTING)) if((m_PlayerFlags & PLAYERFLAG_CHATTING) && (pNewInput->m_PlayerFlags & PLAYERFLAG_CHATTING))
return; return;
@ -520,7 +525,8 @@ void CPlayer::OnPredictedInput(CNetObj_PlayerInput *pNewInput)
GameServer()->SendBroadcast("This server uses an experimental translation from Teeworlds 0.7 to 0.6. Please report bugs on ddnet.org/discord", m_ClientId); GameServer()->SendBroadcast("This server uses an experimental translation from Teeworlds 0.7 to 0.6. Please report bugs on ddnet.org/discord", m_ClientId);
} }
void CPlayer::OnDirectInput(CNetObj_PlayerInput *pNewInput) // Afk player tracking
void CPlayer::OnPlayerFreshInput(CNetObj_PlayerInput *pNewInput)
{ {
Server()->SetClientFlags(m_ClientId, pNewInput->m_PlayerFlags); Server()->SetClientFlags(m_ClientId, pNewInput->m_PlayerFlags);
@ -541,21 +547,6 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *pNewInput)
} }
} }
void CPlayer::OnPredictedEarlyInput(CNetObj_PlayerInput *pNewInput)
{
m_PlayerFlags = pNewInput->m_PlayerFlags;
if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (pNewInput->m_Fire & 1))
m_Spawning = true;
// skip the input if chat is active
if(m_PlayerFlags & PLAYERFLAG_CHATTING)
return;
if(m_pCharacter && !m_Paused)
m_pCharacter->OnDirectInput(pNewInput);
}
int CPlayer::GetClientVersion() const int CPlayer::GetClientVersion() const
{ {
return m_pGameServer->GetClientVersion(m_ClientId); return m_pGameServer->GetClientVersion(m_ClientId);

View file

@ -57,9 +57,8 @@ public:
void Snap(int SnappingClient); void Snap(int SnappingClient);
void FakeSnap(); void FakeSnap();
void OnDirectInput(CNetObj_PlayerInput *pNewInput); void OnPlayerFreshInput(CNetObj_PlayerInput *pNewInput);
void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnPlayerInput(CNetObj_PlayerInput *pNewInput);
void OnPredictedEarlyInput(CNetObj_PlayerInput *pNewInput);
void OnDisconnect(); void OnDisconnect();
void KillCharacter(int Weapon = WEAPON_GAME, bool SendKillMsg = true); void KillCharacter(int Weapon = WEAPON_GAME, bool SendKillMsg = true);