Remove projectiles on save and load

Restructured CSaveTeam a bit, because I also needed access to CGameWorld. I don't store pointer to IGameController in CSaveTeam anymore, because we pass CSaveTeam to the database thread. If it would be accessed there, it could cause a race conditions.
This commit is contained in:
Zwelf 2023-01-03 22:57:31 +01:00
parent 21b3b3b098
commit 73b5d885fa
11 changed files with 138 additions and 94 deletions

View file

@ -801,8 +801,8 @@ void CGameContext::ConDrySave(IConsole::IResult *pResult, void *pUserData)
if(!pPlayer || pSelf->Server()->GetAuthedState(pResult->m_ClientID) != AUTHED_ADMIN)
return;
CSaveTeam SavedTeam(pSelf->m_pController);
int Result = SavedTeam.Save(pPlayer->GetTeam());
CSaveTeam SavedTeam;
int Result = SavedTeam.Save(pSelf, pPlayer->GetTeam(), true);
if(CSaveTeam::HandleSaveError(Result, pResult->m_ClientID, pSelf))
return;

View file

@ -16,6 +16,8 @@ public:
virtual void Snap(int SnappingClient) override;
virtual void SwapClients(int Client1, int Client2) override;
virtual int GetOwnerID() const override { return m_Owner; }
protected:
bool HitCharacter(vec2 From, vec2 To);
void DoBounce();

View file

@ -50,6 +50,8 @@ private:
public:
void SetBouncing(int Value);
bool FillExtraInfo(CNetObj_DDNetProjectile *pProj);
virtual int GetOwnerID() const override { return m_Owner; }
};
#endif

View file

@ -128,6 +128,16 @@ public: // TODO: Maybe make protected
*/
virtual void SwapClients(int Client1, int Client2) {}
/*
Function GetOwnerID
Returns:
ClientID of the initiator from this entity. -1 created by map.
This is used by save/load to remove related entities to the tee.
CCharacter should not return the PlayerId, because they get
handled separatly in save/load code.
*/
virtual int GetOwnerID() const { return -1; }
/*
Function: NetworkClipped
Performs a series of test to see if a client can see the

View file

@ -150,6 +150,26 @@ void CGameWorld::Reset()
GameServer()->CreateAllEntities(false);
}
void CGameWorld::RemoveEntitiesFromPlayers(int PlayerIds[], int NumPlayers)
{
for(auto *pEnt : m_apFirstEntityTypes)
{
for(; pEnt;)
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
for(int i = 0; i < NumPlayers; i++)
{
if(pEnt->GetOwnerID() == PlayerIds[i])
{
RemoveEntity(pEnt);
pEnt->Destroy();
}
}
pEnt = m_pNextTraverseEntity;
}
}
}
void CGameWorld::RemoveEntities()
{
// destroy objects marked for destruction

View file

@ -121,6 +121,8 @@ public:
*/
void RemoveEntity(CEntity *pEntity);
void RemoveEntitiesFromPlayers(int PlayerIds[], int NumPlayers);
/*
Function: Snap
Calls Snap on all the entities in the world to create

View file

@ -2,6 +2,7 @@
#include <cstdio>
#include "engine/shared/protocol.h"
#include "entities/character.h"
#include "gamemodes/DDRace.h"
#include "player.h"
@ -427,11 +428,9 @@ bool CSaveTee::IsHooking() const
return m_HookState == HOOK_GRABBED || m_HookState == HOOK_FLYING;
}
CSaveTeam::CSaveTeam(IGameController *pController)
CSaveTeam::CSaveTeam()
{
m_pController = pController;
m_pSwitchers = 0;
m_pSavedTees = 0;
m_aString[0] = '\0';
}
CSaveTeam::~CSaveTeam()
@ -440,62 +439,67 @@ CSaveTeam::~CSaveTeam()
delete[] m_pSavedTees;
}
int CSaveTeam::Save(int Team)
int CSaveTeam::Save(CGameContext *pGameServer, int Team, bool Dry)
{
if(g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || (Team > 0 && Team < MAX_CLIENTS))
{
CGameTeams *pTeams = &(((CGameControllerDDRace *)m_pController)->m_Teams);
m_MembersCount = pTeams->Count(Team);
if(m_MembersCount <= 0)
{
return 2;
}
m_TeamState = pTeams->GetTeamState(Team);
if(m_TeamState != CGameTeams::TEAMSTATE_STARTED)
{
return 4;
}
m_HighestSwitchNumber = m_pController->GameServer()->Collision()->m_HighestSwitchNumber;
m_TeamLocked = pTeams->TeamLocked(Team);
m_Practice = pTeams->IsPractice(Team);
m_pSavedTees = new CSaveTee[m_MembersCount];
int j = 0;
CCharacter *p = (CCharacter *)m_pController->GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; p; p = (CCharacter *)p->TypeNext())
{
if(pTeams->m_Core.Team(p->GetPlayer()->GetCID()) != Team)
continue;
if(m_MembersCount == j)
return 3;
m_pSavedTees[j].Save(p);
j++;
}
if(m_MembersCount != j)
return 3;
if(m_pController->GameServer()->Collision()->m_HighestSwitchNumber)
{
m_pSwitchers = new SSimpleSwitchers[m_pController->GameServer()->Collision()->m_HighestSwitchNumber + 1];
for(int i = 1; i < m_pController->GameServer()->Collision()->m_HighestSwitchNumber + 1; i++)
{
m_pSwitchers[i].m_Status = m_pController->GameServer()->Switchers()[i].m_aStatus[Team];
if(m_pController->GameServer()->Switchers()[i].m_aEndTick[Team])
m_pSwitchers[i].m_EndTime = m_pController->Server()->Tick() - m_pController->GameServer()->Switchers()[i].m_aEndTick[Team];
else
m_pSwitchers[i].m_EndTime = 0;
m_pSwitchers[i].m_Type = m_pController->GameServer()->Switchers()[i].m_aType[Team];
}
}
return 0;
}
else
if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && (Team <= 0 || MAX_CLIENTS <= Team))
return 1;
IGameController *pController = pGameServer->m_pController;
CGameTeams *pTeams = &(((CGameControllerDDRace *)pController)->m_Teams);
m_MembersCount = pTeams->Count(Team);
if(m_MembersCount <= 0)
{
return 2;
}
m_TeamState = pTeams->GetTeamState(Team);
if(m_TeamState != CGameTeams::TEAMSTATE_STARTED)
{
return 4;
}
m_HighestSwitchNumber = pGameServer->Collision()->m_HighestSwitchNumber;
m_TeamLocked = pTeams->TeamLocked(Team);
m_Practice = pTeams->IsPractice(Team);
m_pSavedTees = new CSaveTee[m_MembersCount];
int aPlayerCIDs[MAX_CLIENTS];
int j = 0;
CCharacter *p = (CCharacter *)pGameServer->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; p; p = (CCharacter *)p->TypeNext())
{
if(pTeams->m_Core.Team(p->GetPlayer()->GetCID()) != Team)
continue;
if(m_MembersCount == j)
return 3;
m_pSavedTees[j].Save(p);
aPlayerCIDs[j] = p->GetPlayer()->GetCID();
j++;
}
if(m_MembersCount != j)
return 3;
if(pGameServer->Collision()->m_HighestSwitchNumber)
{
m_pSwitchers = new SSimpleSwitchers[pGameServer->Collision()->m_HighestSwitchNumber + 1];
for(int i = 1; i < pGameServer->Collision()->m_HighestSwitchNumber + 1; i++)
{
m_pSwitchers[i].m_Status = pGameServer->Switchers()[i].m_aStatus[Team];
if(pGameServer->Switchers()[i].m_aEndTick[Team])
m_pSwitchers[i].m_EndTime = pController->Server()->Tick() - pGameServer->Switchers()[i].m_aEndTick[Team];
else
m_pSwitchers[i].m_EndTime = 0;
m_pSwitchers[i].m_Type = pGameServer->Switchers()[i].m_aType[Team];
}
}
if(!Dry)
{
pGameServer->m_World.RemoveEntitiesFromPlayers(aPlayerCIDs, m_MembersCount);
}
return 0;
}
bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameContext)
@ -523,45 +527,50 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon
return true;
}
void CSaveTeam::Load(int Team, bool KeepCurrentWeakStrong)
void CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong)
{
CGameTeams *pTeams = &(((CGameControllerDDRace *)m_pController)->m_Teams);
IGameController *pController = pGameServer->m_pController;
CGameTeams *pTeams = &(((CGameControllerDDRace *)pController)->m_Teams);
pTeams->ChangeTeamState(Team, m_TeamState);
pTeams->SetTeamLock(Team, m_TeamLocked);
pTeams->SetPractice(Team, m_Practice);
int aPlayerCIDs[MAX_CLIENTS];
for(int i = m_MembersCount; i-- > 0;)
{
int ClientID = m_pSavedTees[i].GetClientID();
if(m_pController->GameServer()->m_apPlayers[ClientID] && pTeams->m_Core.Team(ClientID) == Team)
aPlayerCIDs[i] = ClientID;
if(pGameServer->m_apPlayers[ClientID] && pTeams->m_Core.Team(ClientID) == Team)
{
CCharacter *pChr = MatchCharacter(m_pSavedTees[i].GetClientID(), i, KeepCurrentWeakStrong);
CCharacter *pChr = MatchCharacter(pGameServer, m_pSavedTees[i].GetClientID(), i, KeepCurrentWeakStrong);
m_pSavedTees[i].Load(pChr, Team);
}
}
if(m_pController->GameServer()->Collision()->m_HighestSwitchNumber)
if(pGameServer->Collision()->m_HighestSwitchNumber)
{
for(int i = 1; i < minimum(m_HighestSwitchNumber, m_pController->GameServer()->Collision()->m_HighestSwitchNumber) + 1; i++)
for(int i = 1; i < minimum(m_HighestSwitchNumber, pGameServer->Collision()->m_HighestSwitchNumber) + 1; i++)
{
m_pController->GameServer()->Switchers()[i].m_aStatus[Team] = m_pSwitchers[i].m_Status;
pGameServer->Switchers()[i].m_aStatus[Team] = m_pSwitchers[i].m_Status;
if(m_pSwitchers[i].m_EndTime)
m_pController->GameServer()->Switchers()[i].m_aEndTick[Team] = m_pController->Server()->Tick() - m_pSwitchers[i].m_EndTime;
m_pController->GameServer()->Switchers()[i].m_aType[Team] = m_pSwitchers[i].m_Type;
pGameServer->Switchers()[i].m_aEndTick[Team] = pController->Server()->Tick() - m_pSwitchers[i].m_EndTime;
pGameServer->Switchers()[i].m_aType[Team] = m_pSwitchers[i].m_Type;
}
}
// remove projectiles and laser
pGameServer->m_World.RemoveEntitiesFromPlayers(aPlayerCIDs, m_MembersCount);
}
CCharacter *CSaveTeam::MatchCharacter(int ClientID, int SaveID, bool KeepCurrentCharacter)
CCharacter *CSaveTeam::MatchCharacter(CGameContext *pGameServer, int ClientID, int SaveID, bool KeepCurrentCharacter)
{
if(KeepCurrentCharacter && m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter())
if(KeepCurrentCharacter && pGameServer->m_apPlayers[ClientID]->GetCharacter())
{
// keep old character to retain current weak/strong order
return m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter();
return pGameServer->m_apPlayers[ClientID]->GetCharacter();
}
m_pController->GameServer()->m_apPlayers[ClientID]->KillCharacter(WEAPON_GAME);
return m_pController->GameServer()->m_apPlayers[ClientID]->ForceSpawn(m_pSavedTees[SaveID].GetPos());
pGameServer->m_apPlayers[ClientID]->KillCharacter(WEAPON_GAME);
return pGameServer->m_apPlayers[ClientID]->ForceSpawn(m_pSavedTees[SaveID].GetPos());
}
char *CSaveTeam::GetString()

View file

@ -8,6 +8,7 @@
class IGameController;
class CGameContext;
class CGameWorld;
class CCharacter;
class CSaveTeam;
@ -123,7 +124,7 @@ private:
class CSaveTeam
{
public:
CSaveTeam(IGameController *pController);
CSaveTeam();
~CSaveTeam();
char *GetString();
int GetMembersCount() const { return m_MembersCount; }
@ -131,17 +132,16 @@ public:
int FromString(const char *pString);
// returns true if a team can load, otherwise writes a nice error Message in pMessage
bool MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen);
int Save(int Team);
void Load(int Team, bool KeepCurrentWeakStrong);
CSaveTee *m_pSavedTees;
int Save(CGameContext *pGameServer, int Team, bool Dry = false);
void Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong);
CSaveTee *m_pSavedTees = nullptr;
// returns true if an error occurred
static bool HandleSaveError(int Result, int ClientID, CGameContext *pGameContext);
private:
CCharacter *MatchCharacter(int ClientID, int SaveID, bool KeepCurrentCharacter);
IGameController *m_pController;
CCharacter *MatchCharacter(CGameContext *pGameServer, int ClientID, int SaveID, bool KeepCurrentCharacter);
char m_aString[65536];
@ -151,13 +151,13 @@ private:
int m_EndTime;
int m_Type;
};
SSimpleSwitchers *m_pSwitchers;
SSimpleSwitchers *m_pSwitchers = nullptr;
int m_TeamState;
int m_MembersCount;
int m_HighestSwitchNumber;
int m_TeamLocked;
int m_Practice;
int m_TeamState = 0;
int m_MembersCount = 0;
int m_HighestSwitchNumber = 0;
int m_TeamLocked = 0;
int m_Practice = 0;
};
#endif // GAME_SERVER_SAVE_H

View file

@ -278,9 +278,9 @@ void CScore::SaveTeam(int ClientID, const char *pCode, const char *pServer)
if(pController->m_Teams.GetSaving(Team))
return;
auto SaveResult = std::make_shared<CScoreSaveResult>(ClientID, pController);
auto SaveResult = std::make_shared<CScoreSaveResult>(ClientID);
SaveResult->m_SaveID = RandomUuid();
int Result = SaveResult->m_SavedTeam.Save(Team);
int Result = SaveResult->m_SavedTeam.Save(GameServer(), Team);
if(CSaveTeam::HandleSaveError(Result, ClientID, GameServer()))
return;
pController->m_Teams.SetSaving(Team, SaveResult);
@ -332,7 +332,7 @@ void CScore::LoadTeam(const char *pCode, int ClientID)
GameServer()->SendChatTarget(ClientID, "Team can't be loaded while racing");
return;
}
auto SaveResult = std::make_shared<CScoreSaveResult>(ClientID, pController);
auto SaveResult = std::make_shared<CScoreSaveResult>(ClientID);
SaveResult->m_Status = CScoreSaveResult::LOAD_FAILED;
pController->m_Teams.SetSaving(Team, SaveResult);
auto Tmp = std::make_unique<CSqlTeamLoad>(SaveResult);

View file

@ -149,9 +149,8 @@ struct CSqlScoreData : ISqlData
struct CScoreSaveResult : ISqlResult
{
CScoreSaveResult(int PlayerID, IGameController *pController) :
CScoreSaveResult(int PlayerID) :
m_Status(SAVE_FAILED),
m_SavedTeam(CSaveTeam(pController)),
m_RequestingPlayer(PlayerID)
{
m_aMessage[0] = '\0';

View file

@ -977,7 +977,7 @@ void CGameTeams::ProcessSaveTeam()
if(Count(Team) > 0)
{
// load weak/strong order to prevent switching weak/strong while saving
m_apSaveTeamResult[Team]->m_SavedTeam.Load(Team, false);
m_apSaveTeamResult[Team]->m_SavedTeam.Load(GameServer(), Team, false);
}
break;
case CScoreSaveResult::LOAD_SUCCESS:
@ -992,7 +992,7 @@ void CGameTeams::ProcessSaveTeam()
if(Count(Team) > 0)
{
// keep current weak/strong order as on some maps there is no other way of switching
m_apSaveTeamResult[Team]->m_SavedTeam.Load(Team, true);
m_apSaveTeamResult[Team]->m_SavedTeam.Load(GameServer(), Team, true);
}
char aSaveID[UUID_MAXSTRSIZE];
FormatUuid(m_apSaveTeamResult[Team]->m_SaveID, aSaveID, UUID_MAXSTRSIZE);