2932: Don't change SpectatorID when not spectating r=def- a=Fireball-Teeworlds

This prevents a confusing situation where /pause and /spec lead to spectating yourself: https://youtu.be/9MzvDoXtMLc (looks as if /pause is broken unless you notice that you are spectating yourself).

This could've happened when you pressed spectate_next button while playing and SpectatorID got changed to yourself. I think it doesn't make sense to allow changing SpectatorID when playing at all.

I've also tried to simplify the code: the number of `for` loops in the updated code goes down from 10 to 3.

Things tested with this change:
1. Cycling through players when spectating (works)
2. Changing SpectatorID when not spectating (no longer possible)
3. Spectating yourself (still possible with "spectate N" command but no longer happens accidentally because of 2)
4. "spectate_closest" command (works)
5. Replaying demos where player goes into spectators (works)


Co-authored-by: Fireball <fireball.teeworlds@gmail.com>
This commit is contained in:
bors[bot] 2020-10-02 07:55:30 +00:00 committed by GitHub
commit 74550e2c45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 110 deletions

View file

@ -17,6 +17,64 @@
#include "camera.h"
#include "spectator.h"
bool CSpectator::CanChangeSpectator()
{
// Don't change SpectatorID when not spectating
return m_pClient->m_Snap.m_SpecInfo.m_Active;
}
void CSpectator::SpectateNext(bool Reverse)
{
int CurIndex = -1;
const CNetObj_PlayerInfo **paPlayerInfos = m_pClient->m_Snap.m_paInfoByDDTeamName;
// m_SpectatorID may be uninitialized if m_Active is false
if(m_pClient->m_Snap.m_SpecInfo.m_Active)
{
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(paPlayerInfos[i] && paPlayerInfos[i]->m_ClientID == m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)
{
CurIndex = i;
break;
}
}
}
int Start;
if(CurIndex != -1)
{
if(Reverse)
Start = CurIndex - 1;
else
Start = CurIndex + 1;
}
else
{
if(Reverse)
Start = -1;
else
Start = 0;
}
int Increment = Reverse ? -1 : 1;
for(int i = 0; i < MAX_CLIENTS; i++)
{
int PlayerIndex = (Start + i * Increment) % MAX_CLIENTS;
// % in C++ takes the sign of the dividend, not divisor
if(PlayerIndex < 0)
PlayerIndex += MAX_CLIENTS;
const CNetObj_PlayerInfo *pPlayerInfo = paPlayerInfos[PlayerIndex];
if(pPlayerInfo && pPlayerInfo->m_Team != TEAM_SPECTATORS)
{
Spectate(pPlayerInfo->m_ClientID);
break;
}
}
}
void CSpectator::ConKeySpectator(IConsole::IResult *pResult, void *pUserData)
{
CSpectator *pSelf = (CSpectator *)pUserData;
@ -29,145 +87,60 @@ void CSpectator::ConKeySpectator(IConsole::IResult *pResult, void *pUserData)
void CSpectator::ConSpectate(IConsole::IResult *pResult, void *pUserData)
{
((CSpectator *)pUserData)->Spectate(pResult->GetInteger(0));
CSpectator *pSelf = (CSpectator *)pUserData;
if(!pSelf->CanChangeSpectator())
return;
pSelf->Spectate(pResult->GetInteger(0));
}
void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData)
{
CSpectator *pSelf = (CSpectator *)pUserData;
int NewSpectatorID;
bool GotNewSpectatorID = false;
if(!pSelf->CanChangeSpectator())
return;
int CurPos = -1;
for(int i = 0; i < MAX_CLIENTS; i++)
if(pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] && pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID == pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)
CurPos = i;
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW)
{
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
}
else
{
for(int i = CurPos + 1; i < MAX_CLIENTS; i++)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
if(!GotNewSpectatorID)
{
for(int i = 0; i < CurPos; i++)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
}
}
if(GotNewSpectatorID)
pSelf->Spectate(NewSpectatorID);
pSelf->SpectateNext(false);
}
void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData)
{
CSpectator *pSelf = (CSpectator *)pUserData;
int NewSpectatorID;
bool GotNewSpectatorID = false;
if(!pSelf->CanChangeSpectator())
return;
int CurPos = -1;
for(int i = 0; i < MAX_CLIENTS; i++)
if(pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] && pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID == pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)
CurPos = i;
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW)
{
for(int i = MAX_CLIENTS - 1; i > -1; i--)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
}
else
{
for(int i = CurPos - 1; i > -1; i--)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
if(!GotNewSpectatorID)
{
for(int i = MAX_CLIENTS - 1; i > CurPos; i--)
{
if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
continue;
NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
GotNewSpectatorID = true;
break;
}
}
}
if(GotNewSpectatorID)
pSelf->Spectate(NewSpectatorID);
pSelf->SpectateNext(true);
}
void CSpectator::ConSpectateClosest(IConsole::IResult *pResult, void *pUserData)
{
CSpectator *pSelf = (CSpectator *)pUserData;
if(!pSelf->CanChangeSpectator())
return;
CGameClient::CSnapState &Snap = pSelf->m_pClient->m_Snap;
int SpectatorID = Snap.m_SpecInfo.m_SpectatorID;
int NewSpectatorID = -1;
int IndexOfTeeBeingSpectated = -1;
vec2 CurPosition(pSelf->m_pClient->m_pCamera->m_Center);
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW)
if(SpectatorID != SPEC_FREEVIEW)
{
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] && pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID == pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)
{
IndexOfTeeBeingSpectated = i;
const CNetObj_Character &CurCharacter = pSelf->m_pClient->m_Snap.m_aCharacters[pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID].m_Cur;
CurPosition.x = CurCharacter.m_X;
CurPosition.y = CurCharacter.m_Y;
break;
}
}
const CNetObj_Character &CurCharacter = Snap.m_aCharacters[SpectatorID].m_Cur;
CurPosition.x = CurCharacter.m_X;
CurPosition.y = CurCharacter.m_Y;
}
int ClosestDistance = INT_MAX;
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(i == IndexOfTeeBeingSpectated || !pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_Team == TEAM_SPECTATORS)
if(i == SpectatorID || !Snap.m_paPlayerInfos[i] || Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS)
continue;
int ClientID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeamName[i]->m_ClientID;
const CNetObj_Character &MaybeClosestCharacter = pSelf->m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur;
const CNetObj_Character &MaybeClosestCharacter = Snap.m_aCharacters[i].m_Cur;
int Distance = distance(CurPosition, vec2(MaybeClosestCharacter.m_X, MaybeClosestCharacter.m_Y));
if(NewSpectatorID == -1 || Distance < ClosestDistance)
{
NewSpectatorID = ClientID;
NewSpectatorID = i;
ClosestDistance = Distance;
}
}

View file

@ -22,6 +22,9 @@ class CSpectator : public CComponent
float m_OldMouseX;
float m_OldMouseY;
bool CanChangeSpectator();
void SpectateNext(bool Reverse);
static void ConKeySpectator(IConsole::IResult *pResult, void *pUserData);
static void ConSpectate(IConsole::IResult *pResult, void *pUserData);
static void ConSpectateNext(IConsole::IResult *pResult, void *pUserData);