mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-09 09:38:19 +00:00
Merge pull request #8813 from furo321/hot-reload-maps
Add `hot_reload` to reload map while preserving state
This commit is contained in:
commit
29f3323735
|
@ -254,6 +254,7 @@ public:
|
|||
virtual void Ban(int ClientId, int Seconds, const char *pReason, bool VerbatimReason) = 0;
|
||||
virtual void RedirectClient(int ClientId, int Port, bool Verbose = false) = 0;
|
||||
virtual void ChangeMap(const char *pMap) = 0;
|
||||
virtual void ReloadMap() = 0;
|
||||
|
||||
virtual void DemoRecorder_HandleAutoStart() = 0;
|
||||
|
||||
|
|
|
@ -2551,6 +2551,11 @@ void CServer::ChangeMap(const char *pMap)
|
|||
m_MapReload = str_comp(Config()->m_SvMap, m_aCurrentMap) != 0;
|
||||
}
|
||||
|
||||
void CServer::ReloadMap()
|
||||
{
|
||||
m_MapReload = true;
|
||||
}
|
||||
|
||||
int CServer::LoadMap(const char *pMapName)
|
||||
{
|
||||
m_MapReload = false;
|
||||
|
|
|
@ -379,6 +379,7 @@ public:
|
|||
|
||||
void ChangeMap(const char *pMap) override;
|
||||
const char *GetMapName() const override;
|
||||
void ReloadMap() override;
|
||||
int LoadMap(const char *pMapName);
|
||||
|
||||
void SaveDemo(int ClientId, float Time) override;
|
||||
|
|
|
@ -8616,7 +8616,7 @@ void CEditor::HandleWriterFinishJobs()
|
|||
char aMapName[128];
|
||||
IStorage::StripPathAndExtension(pJob->GetRealFileName(), aMapName, sizeof(aMapName));
|
||||
if(!str_comp(aMapName, CurrentServerInfo.m_aMap))
|
||||
Client()->Rcon("reload");
|
||||
Client()->Rcon("hot_reload");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,28 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos)
|
|||
TrySetRescue(RESCUEMODE_MANUAL);
|
||||
Server()->StartRecord(m_pPlayer->GetCid());
|
||||
|
||||
int Team = GameServer()->m_aTeamMapping[m_pPlayer->GetCid()];
|
||||
|
||||
if(Team != -1)
|
||||
{
|
||||
GameServer()->m_pController->Teams().SetForceCharacterTeam(m_pPlayer->GetCid(), Team);
|
||||
GameServer()->m_aTeamMapping[m_pPlayer->GetCid()] = -1;
|
||||
|
||||
if(GameServer()->m_apSavedTeams[Team])
|
||||
{
|
||||
GameServer()->m_apSavedTeams[Team]->Load(GameServer(), Team, true, true);
|
||||
delete GameServer()->m_apSavedTeams[Team];
|
||||
GameServer()->m_apSavedTeams[Team] = nullptr;
|
||||
}
|
||||
|
||||
if(GameServer()->m_apSavedTees[m_pPlayer->GetCid()])
|
||||
{
|
||||
GameServer()->m_apSavedTees[m_pPlayer->GetCid()]->Load(m_pPlayer->GetCharacter(), Team);
|
||||
delete GameServer()->m_apSavedTees[Team];
|
||||
GameServer()->m_apSavedTees[m_pPlayer->GetCid()] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,14 @@ void CGameContext::Construct(int Resetting)
|
|||
|
||||
if(Resetting == NO_RESET)
|
||||
{
|
||||
for(auto &pSavedTee : m_apSavedTees)
|
||||
pSavedTee = nullptr;
|
||||
|
||||
for(auto &pSavedTeam : m_apSavedTeams)
|
||||
pSavedTeam = nullptr;
|
||||
|
||||
std::fill(std::begin(m_aTeamMapping), std::end(m_aTeamMapping), -1);
|
||||
|
||||
m_NonEmptySince = 0;
|
||||
m_pVoteOptionHeap = new CHeap();
|
||||
}
|
||||
|
@ -119,7 +127,15 @@ void CGameContext::Destruct(int Resetting)
|
|||
delete pPlayer;
|
||||
|
||||
if(Resetting == NO_RESET)
|
||||
{
|
||||
for(auto &pSavedTee : m_apSavedTees)
|
||||
delete pSavedTee;
|
||||
|
||||
for(auto &pSavedTeam : m_apSavedTeams)
|
||||
delete pSavedTeam;
|
||||
|
||||
delete m_pVoteOptionHeap;
|
||||
}
|
||||
|
||||
if(m_pScore)
|
||||
{
|
||||
|
@ -1710,6 +1726,14 @@ void CGameContext::OnClientDrop(int ClientId, const char *pReason)
|
|||
delete m_apPlayers[ClientId];
|
||||
m_apPlayers[ClientId] = 0;
|
||||
|
||||
delete m_apSavedTeams[ClientId];
|
||||
m_apSavedTeams[ClientId] = nullptr;
|
||||
|
||||
delete m_apSavedTees[ClientId];
|
||||
m_apSavedTees[ClientId] = nullptr;
|
||||
|
||||
m_aTeamMapping[ClientId] = -1;
|
||||
|
||||
m_VoteUpdate = true;
|
||||
|
||||
// update spectator modes
|
||||
|
@ -3194,6 +3218,30 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData)
|
|||
pSelf->m_pController->DoTeamChange(pPlayer, Team, false);
|
||||
}
|
||||
|
||||
void CGameContext::ConHotReload(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *)pUserData;
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(!pSelf->GetPlayerChar(i))
|
||||
continue;
|
||||
|
||||
// Save the tee individually
|
||||
pSelf->m_apSavedTees[i] = new CSaveTee();
|
||||
pSelf->m_apSavedTees[i]->Save(pSelf->GetPlayerChar(i), false);
|
||||
|
||||
// Save the team state
|
||||
pSelf->m_aTeamMapping[i] = pSelf->GetDDRaceTeam(i);
|
||||
|
||||
if(pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]])
|
||||
continue;
|
||||
|
||||
pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]] = new CSaveTeam();
|
||||
pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]]->Save(pSelf, pSelf->m_aTeamMapping[i], true, true);
|
||||
}
|
||||
pSelf->Server()->ReloadMap();
|
||||
}
|
||||
|
||||
void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *)pUserData;
|
||||
|
@ -3552,6 +3600,7 @@ void CGameContext::OnConsoleInit()
|
|||
Console()->Register("say", "r[message]", CFGFLAG_SERVER, ConSay, this, "Say in chat");
|
||||
Console()->Register("set_team", "i[id] i[team-id] ?i[delay in minutes]", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team");
|
||||
Console()->Register("set_team_all", "i[team-id]", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team");
|
||||
Console()->Register("hot_reload", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConHotReload, this, "Reload the map while preserving the state of tees and teams");
|
||||
|
||||
Console()->Register("add_vote", "s[name] r[command]", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option");
|
||||
Console()->Register("remove_vote", "r[name]", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option");
|
||||
|
|
|
@ -124,6 +124,7 @@ class CGameContext : public IGameServer
|
|||
static void ConSay(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConSetTeam(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConSetTeamAll(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConHotReload(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConAddVote(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConRemoveVote(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConForceVote(IConsole::IResult *pResult, void *pUserData);
|
||||
|
@ -180,6 +181,9 @@ public:
|
|||
// keep last input to always apply when none is sent
|
||||
CNetObj_PlayerInput m_aLastPlayerInput[MAX_CLIENTS];
|
||||
bool m_aPlayerHasInput[MAX_CLIENTS];
|
||||
CSaveTeam *m_apSavedTeams[MAX_CLIENTS];
|
||||
CSaveTee *m_apSavedTees[MAX_CLIENTS];
|
||||
int m_aTeamMapping[MAX_CLIENTS];
|
||||
|
||||
// returns last input if available otherwise nulled PlayerInput object
|
||||
// ClientId has to be valid
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
CSaveTee::CSaveTee() = default;
|
||||
|
||||
void CSaveTee::Save(CCharacter *pChr)
|
||||
void CSaveTee::Save(CCharacter *pChr, bool AddPenalty)
|
||||
{
|
||||
m_ClientId = pChr->m_pPlayer->GetCid();
|
||||
str_copy(m_aName, pChr->Server()->ClientName(m_ClientId), sizeof(m_aName));
|
||||
|
@ -75,10 +75,13 @@ void CSaveTee::Save(CCharacter *pChr)
|
|||
m_TuneZoneOld = pChr->m_TuneZoneOld;
|
||||
|
||||
if(pChr->m_StartTime)
|
||||
m_Time = pChr->Server()->Tick() - pChr->m_StartTime + g_Config.m_SvSaveSwapGamesPenalty * pChr->Server()->TickSpeed();
|
||||
m_Time = pChr->Server()->Tick() - pChr->m_StartTime;
|
||||
else
|
||||
m_Time = 0;
|
||||
|
||||
if(AddPenalty && pChr->m_StartTime)
|
||||
m_Time += g_Config.m_SvSaveSwapGamesPenalty * pChr->Server()->TickSpeed();
|
||||
|
||||
m_Pos = pChr->m_Pos;
|
||||
m_PrevPos = pChr->m_PrevPos;
|
||||
m_TeleCheckpoint = pChr->m_TeleCheckpoint;
|
||||
|
@ -490,28 +493,28 @@ CSaveTeam::~CSaveTeam()
|
|||
delete[] m_pSavedTees;
|
||||
}
|
||||
|
||||
ESaveResult CSaveTeam::Save(CGameContext *pGameServer, int Team, bool Dry)
|
||||
ESaveResult CSaveTeam::Save(CGameContext *pGameServer, int Team, bool Dry, bool Force)
|
||||
{
|
||||
if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && (Team <= 0 || MAX_CLIENTS <= Team))
|
||||
if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && (Team <= 0 || MAX_CLIENTS <= Team) && !Force)
|
||||
return ESaveResult::TEAM_FLOCK;
|
||||
|
||||
IGameController *pController = pGameServer->m_pController;
|
||||
CGameTeams *pTeams = &pController->Teams();
|
||||
|
||||
if(pTeams->TeamFlock(Team))
|
||||
if(pTeams->TeamFlock(Team) && !Force)
|
||||
{
|
||||
return ESaveResult::TEAM_0_MODE;
|
||||
}
|
||||
|
||||
m_MembersCount = pTeams->Count(Team);
|
||||
if(m_MembersCount <= 0)
|
||||
if(m_MembersCount <= 0 && !Force)
|
||||
{
|
||||
return ESaveResult::TEAM_NOT_FOUND;
|
||||
}
|
||||
|
||||
m_TeamState = pTeams->GetTeamState(Team);
|
||||
|
||||
if(m_TeamState != CGameTeams::TEAMSTATE_STARTED)
|
||||
if(m_TeamState != CGameTeams::TEAMSTATE_STARTED && !Force)
|
||||
{
|
||||
return ESaveResult::NOT_STARTED;
|
||||
}
|
||||
|
@ -537,7 +540,7 @@ ESaveResult CSaveTeam::Save(CGameContext *pGameServer, int Team, bool Dry)
|
|||
aPlayerCids[j] = p->GetPlayer()->GetCid();
|
||||
j++;
|
||||
}
|
||||
if(m_MembersCount != j)
|
||||
if(m_MembersCount != j && !Force)
|
||||
return ESaveResult::CHAR_NOT_FOUND;
|
||||
|
||||
if(pGameServer->Collision()->m_HighestSwitchNumber)
|
||||
|
@ -589,7 +592,7 @@ bool CSaveTeam::HandleSaveError(ESaveResult Result, int ClientId, CGameContext *
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong)
|
||||
bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong, bool IgnorePlayers)
|
||||
{
|
||||
IGameController *pController = pGameServer->m_pController;
|
||||
CGameTeams *pTeams = &pController->Teams();
|
||||
|
@ -600,6 +603,9 @@ bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt
|
|||
|
||||
bool ContainsInvalidPlayer = false;
|
||||
int aPlayerCids[MAX_CLIENTS];
|
||||
|
||||
if(!IgnorePlayers)
|
||||
{
|
||||
for(int i = m_MembersCount; i-- > 0;)
|
||||
{
|
||||
int ClientId = m_pSavedTees[i].GetClientId();
|
||||
|
@ -610,6 +616,7 @@ bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt
|
|||
ContainsInvalidPlayer |= !m_pSavedTees[i].Load(pChr, Team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pGameServer->Collision()->m_HighestSwitchNumber)
|
||||
{
|
||||
|
@ -622,6 +629,7 @@ bool CSaveTeam::Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakSt
|
|||
}
|
||||
}
|
||||
// remove projectiles and laser
|
||||
if(!IgnorePlayers)
|
||||
pGameServer->m_World.RemoveEntitiesFromPlayers(aPlayerCids, m_MembersCount);
|
||||
|
||||
return !ContainsInvalidPlayer;
|
||||
|
|
|
@ -35,7 +35,7 @@ class CSaveTee
|
|||
public:
|
||||
CSaveTee();
|
||||
~CSaveTee() = default;
|
||||
void Save(CCharacter *pchr);
|
||||
void Save(CCharacter *pchr, bool AddPenalty = true);
|
||||
bool Load(CCharacter *pchr, int Team, bool IsSwap = false);
|
||||
char *GetString(const CSaveTeam *pTeam);
|
||||
int FromString(const char *pString);
|
||||
|
@ -159,8 +159,8 @@ 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) const;
|
||||
ESaveResult Save(CGameContext *pGameServer, int Team, bool Dry = false);
|
||||
bool Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong);
|
||||
ESaveResult Save(CGameContext *pGameServer, int Team, bool Dry = false, bool Force = false);
|
||||
bool Load(CGameContext *pGameServer, int Team, bool KeepCurrentWeakStrong, bool IgnorePlayers = false);
|
||||
|
||||
CSaveTee *m_pSavedTees = nullptr;
|
||||
|
||||
|
|
Loading…
Reference in a new issue