Make empty-server restarts configurable in the mission menu
Add persistent last-disconnect and idle-interval controls with enabled/five-hour defaults. Apply saved settings immediately, reset idle time while occupied, and cover migration, routing, layout and restart behavior.
This commit is contained in:
@@ -8,7 +8,7 @@ class ERR_AdminMenuFields
|
||||
{
|
||||
static array<string> Keys()
|
||||
{
|
||||
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
|
||||
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
|
||||
}
|
||||
static bool Available(string key, bool hq, bool intel)
|
||||
{
|
||||
@@ -64,6 +64,7 @@ class ERR_AdminMenuFields
|
||||
else if (key == "fieldCampCount") prefix = "mission camps";
|
||||
else if (key == "fieldHospitalCount") prefix = "mission hospitals";
|
||||
else if (key == "roadblockCount") prefix = "mission roadblocks";
|
||||
else if (key == "emptyRestartHours") prefix = "mission emptyhours";
|
||||
else if (key == "civilianTrafficCount") prefix = "mission traffic";
|
||||
else if (key == "startMode") prefix = "mission start";
|
||||
else if (key == "radioDefenceSeconds") prefix = "mission radio";
|
||||
@@ -134,6 +135,7 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
names.Insert("RandomDay_startTimeMinutes");
|
||||
names.Insert("Default_viewDistanceMetres");
|
||||
names.Insert("OpenForces");
|
||||
names.Insert("RestartEmptyOn"); names.Insert("RestartEmptyOff");
|
||||
names.Insert("OpenGuards"); names.Insert("GuardsBack");
|
||||
names.Insert("OpenSites"); names.Insert("SitesBack");
|
||||
names.Insert("OpenPlane"); names.Insert("PlaneBack");
|
||||
@@ -255,9 +257,11 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
if (page == 2) m_LogPoll = 1;
|
||||
}
|
||||
protected int m_KeepWeaponState = -1;
|
||||
protected int m_RestartEmptyState = -1;
|
||||
protected void UpdateActions()
|
||||
{
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "KeepWeaponOn", "KeepWeaponOff", m_KeepWeaponState);
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "RestartEmptyOn", "RestartEmptyOff", m_RestartEmptyState);
|
||||
foreach (SCR_ButtonBaseComponent button : m_Buttons)
|
||||
{
|
||||
string name = button.GetRootWidget().GetName();
|
||||
@@ -272,6 +276,8 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
else if (name.IndexOf("GuardHQ_") == 0) button.SetEnabled(m_Ready && m_HQ && m_AvailableFields.Contains("objectGuardHQMode"));
|
||||
else if (name == "RerollIntel" || name == "IntelSummary") button.SetEnabled(m_Ready && m_Intel);
|
||||
else if (name == "LatestJournal") button.SetEnabled(m_Ready && m_HQ);
|
||||
else if (name == "RestartEmptyOn") button.SetEnabled(m_Ready && m_RestartEmptyState == 0);
|
||||
else if (name == "RestartEmptyOff") button.SetEnabled(m_Ready && m_RestartEmptyState == 1);
|
||||
else if (name == "KeepWeaponOn") button.SetEnabled(m_Ready && m_KeepWeaponState == 0);
|
||||
else if (name == "KeepWeaponOff") button.SetEnabled(m_Ready && m_KeepWeaponState == 1);
|
||||
}
|
||||
@@ -280,7 +286,7 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
{
|
||||
if (!authorized) { Close(); return; }
|
||||
if (!values) return;
|
||||
m_HQ = false; m_Intel = false; m_AvailableFields.Clear(); m_KeepWeaponState = -1;
|
||||
m_HQ = false; m_Intel = false; m_AvailableFields.Clear(); m_KeepWeaponState = -1; m_RestartEmptyState = -1;
|
||||
foreach (string entry : values)
|
||||
{
|
||||
if (entry == "supports=hq") { m_HQ = true; continue; }
|
||||
@@ -316,6 +322,14 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "PlaneOn", "PlaneOff", planeState);
|
||||
m_AvailableFields.Insert(key); continue;
|
||||
}
|
||||
if (key == "restartOnEmpty")
|
||||
{
|
||||
m_RestartEmptyState = entry.Substring(divider + 1, entry.Length() - divider - 1).ToInt();
|
||||
string restartState = "Disabled";
|
||||
if (m_RestartEmptyState == 1) restartState = "Enabled";
|
||||
SetText("Saved_restartOnEmpty", "Saved: " + restartState);
|
||||
continue;
|
||||
}
|
||||
if (key == "keepWeaponOnUnconscious")
|
||||
{
|
||||
m_KeepWeaponState = entry.Substring(divider + 1, entry.Length() - divider - 1).ToInt();
|
||||
@@ -464,6 +478,13 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
m_ClickDelay = 0.3; SetText("Feedback", "Saving reconnaissance plane selection for next round...");
|
||||
m_Controller.ERR_AdminSubmit("mission plane set " + enabled, true); return;
|
||||
}
|
||||
if (name == "RestartEmptyOn" || name == "RestartEmptyOff")
|
||||
{
|
||||
if (m_RestartEmptyState < 0) return;
|
||||
string restartEnabled = "0"; if (name == "RestartEmptyOn") restartEnabled = "1";
|
||||
m_ClickDelay = 0.3; SetText("Feedback", "Saving restart after last disconnect...");
|
||||
m_Controller.ERR_AdminSubmit("mission restartempty set " + restartEnabled, true); return;
|
||||
}
|
||||
if (name == "KeepWeaponOn" || name == "KeepWeaponOff")
|
||||
{
|
||||
string retain = "0"; if (name == "KeepWeaponOn") retain = "1";
|
||||
|
||||
@@ -23,6 +23,8 @@ class ERR_CommandArguments
|
||||
if (section == "mission" && option == "traffic") return "civilianTrafficCount";
|
||||
if (section == "mission" && option == "start") return "startMode";
|
||||
if (section == "mission" && option == "keepweapon") return "keepWeaponOnUnconscious";
|
||||
if (section == "mission" && option == "restartempty") return "restartOnEmpty";
|
||||
if (section == "mission" && option == "emptyhours") return "emptyRestartHours";
|
||||
if (section == "mission" && option == "radio") return "radioDefenceSeconds";
|
||||
if (section == "intel" && option == "reveal") return "intelRevealChancePercent";
|
||||
return "";
|
||||
@@ -31,8 +33,8 @@ class ERR_CommandArguments
|
||||
{
|
||||
if (key == "aiDoctrine" && text == "current") { value = 0; return true; }
|
||||
if (key == "aiDoctrine" && text == "coordinated") { value = 1; return true; }
|
||||
if (key == "aiShadow" && text == "off") { value = 0; return true; }
|
||||
if (key == "aiShadow" && text == "on") { value = 1; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty") && text == "off") { value = 0; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty") && text == "on") { value = 1; return true; }
|
||||
if (key == "startMode" && text == "prison") { value = 0; return true; }
|
||||
if (key == "startMode" && text == "crash") { value = 1; return true; }
|
||||
if (text == "random" && ERR_Settings.Randomizable(key)) { value = -1; return true; }
|
||||
@@ -179,6 +181,8 @@ class ERR_AdminService
|
||||
if (key == "objectGuardCount") return "0..16 soldiers per factory/industrial, military or infrastructure site; default 8, zero off. Next round. HQ participation is configured separately; all report. Towers and chimneys use suitable platforms. Lost guards are not replaced.";
|
||||
if (key == "hunterSpawnDistanceMetres") return "Opening UAZ and troop-truck patrol placement. Applies next round.";
|
||||
if (key == "roadblockCount") return "Set 0..30 roadblocks (default 4; 0 disables). Applies next round. Each has four guards; unsuitable sites are skipped.";
|
||||
if (key == "restartOnEmpty") return "On (1) restarts after the last disconnect; Off (0) preserves the round for reconnects. Default On. Applies immediately; normal round endings still apply.";
|
||||
if (key == "emptyRestartHours") return "0 disables idle restarts; 1..168 real hours continuously empty. Default 5. Applies immediately, measured from mission start or the last disconnect. Connected players, including spectators, prevent idle restarts.";
|
||||
if (key == "civilianTrafficCount") return "Set 0..6 civilian cars (default 3). Zero disables new traffic. Unclaimed cars retire only far from all players and out of sight; player-entered cars are preserved.";
|
||||
if (key == "keepWeaponOnUnconscious") return "Set 1 to retain the held firearm during unconsciousness and equip it after recovery; 0 keeps normal dropping. Applies to the next knockout.";
|
||||
if (key == "antiAirChancePercent" || key == "antiPersonnelMineChancePercent")
|
||||
@@ -220,6 +224,7 @@ class ERR_AdminService
|
||||
if (topic == "time" || topic == "mission")
|
||||
{
|
||||
replies.Insert("/esc mission radio show|set N|random | mission abandon");
|
||||
replies.Insert("/esc mission restartempty show|set on|off | mission emptyhours show|set 0..168; applies now, zero hours disables idle restarts.");
|
||||
replies.Insert("/esc mission doctrine show|set current|coordinated | mission aishadow show|set off|on; next round only.");
|
||||
replies.Insert("/esc mission start show|set prison|crash: save the next round start; current round unchanged.");
|
||||
replies.Insert("/esc mission startweapons show|set 0..12 | mission startammo show|set 0..48; shared backpack, next round.");
|
||||
|
||||
@@ -75,7 +75,7 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
protected bool m_StartedPlayers;
|
||||
protected bool m_HadConnectedPlayers;
|
||||
protected bool m_EmptyRestartRequested;
|
||||
protected int m_EmptyMissionStartedAt;
|
||||
protected int m_EmptySince;
|
||||
protected ref map<int, PlayerController> m_ExhaustedInitialAdmissions = new map<int, PlayerController>();
|
||||
protected bool m_Stopped;
|
||||
protected bool m_NativeEndHandled;
|
||||
@@ -458,11 +458,12 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
{
|
||||
if (!Replication.IsServer() || m_Stopped || m_NativeEndHandled) return;
|
||||
m_HadConnectedPlayers = true;
|
||||
m_EmptySince = System.GetTickCount();
|
||||
}
|
||||
|
||||
protected bool CheckEmptyServerRestart()
|
||||
{
|
||||
if (!Replication.IsServer() || m_Stopped || m_NativeEndHandled) return false;
|
||||
if (!Replication.IsServer() || m_Stopped || m_NativeEndHandled || !m_Settings) return false;
|
||||
if (m_EmptyRestartRequested) return true;
|
||||
PlayerManager manager = GetGame().GetPlayerManager();
|
||||
if (!manager) return false;
|
||||
@@ -470,13 +471,16 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
if (manager.GetPlayerCount() > 0)
|
||||
{
|
||||
m_HadConnectedPlayers = true;
|
||||
m_EmptySince = System.GetTickCount();
|
||||
return false;
|
||||
}
|
||||
// Real elapsed milliseconds: mission clock settings cannot accelerate this.
|
||||
if (!m_HadConnectedPlayers && System.GetTickCount() - m_EmptyMissionStartedAt < 18000000) return false;
|
||||
bool afterDisconnect = m_HadConnectedPlayers && m_Settings.Get("restartOnEmpty") == 1;
|
||||
int hours = m_Settings.Get("emptyRestartHours");
|
||||
if (!afterDisconnect && (hours == 0 || System.GetTickCount() - m_EmptySince < hours * 3600000)) return false;
|
||||
m_EmptyRestartRequested = true;
|
||||
m_EndReason = "The server has been empty for five hours. Restarting the mission.";
|
||||
if (m_HadConnectedPlayers) m_EndReason = "All players disconnected. Restarting the mission.";
|
||||
m_EndReason = string.Format("The server has been empty for %1 hours. Restarting the mission.", hours);
|
||||
if (afterDisconnect) m_EndReason = "All players disconnected. Restarting the mission.";
|
||||
PrintFormat("Reintroduced [round lifecycle]: %1", m_EndReason);
|
||||
// Native termination also handles PREPARING, before Transition is available.
|
||||
// OnGameModeEnd stops our services; vanilla owns the scenario reload.
|
||||
@@ -492,7 +496,7 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
if (!System.IsConsoleApp()) GetGame().GetCallqueue().CallLater(BindLocalObserver, 250, true);
|
||||
if (!System.IsConsoleApp()) GetGame().GetCallqueue().CallLater(ApplyLocalViewDistance, 1000, true);
|
||||
if (!Replication.IsServer()) return;
|
||||
m_EmptyMissionStartedAt = System.GetTickCount();
|
||||
m_EmptySince = System.GetTickCount();
|
||||
m_Settings = new ERR_Settings(SettingsPath());
|
||||
if (!System.IsCLIParam("errNoPerf")) m_Performance = new ERR_Performance();
|
||||
PublishViewDistance();
|
||||
|
||||
@@ -9,7 +9,7 @@ class ERR_Settings
|
||||
|
||||
static array<string> Keys()
|
||||
{
|
||||
return {"aiDoctrine", "aiShadow", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
|
||||
return {"aiDoctrine", "aiShadow", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
|
||||
}
|
||||
|
||||
static bool Valid(string key, int value)
|
||||
@@ -31,7 +31,8 @@ class ERR_Settings
|
||||
if (key == "roadblockCount") return value >= 0 && value <= 30;
|
||||
if (key == "civilianTrafficCount") return value >= 0 && value <= 6;
|
||||
if (key == "startMode") return value == 0 || value == 1;
|
||||
if (key == "keepWeaponOnUnconscious") return value == 0 || value == 1;
|
||||
if (key == "keepWeaponOnUnconscious" || key == "restartOnEmpty") return value == 0 || value == 1;
|
||||
if (key == "emptyRestartHours") return value >= 0 && value <= 168;
|
||||
if (key == "heliTimerMinutes") return value >= 1 && value <= 1440;
|
||||
if (key == "convoyCount") return value >= 0 && value <= 20;
|
||||
if (key == "automaticConvoyEnabled") return value == 0 || value == 1;
|
||||
@@ -132,7 +133,8 @@ class ERR_Settings
|
||||
if (key == "roadblockCount") return 4;
|
||||
if (key == "civilianTrafficCount") return 3;
|
||||
if (key == "startMode") return 0;
|
||||
if (key == "keepWeaponOnUnconscious") return 1;
|
||||
if (key == "keepWeaponOnUnconscious" || key == "restartOnEmpty") return 1;
|
||||
if (key == "emptyRestartHours") return 5;
|
||||
if (key == "heliTimerMinutes") return 15;
|
||||
if (key == "convoyCount") return 3;
|
||||
if (key == "automaticConvoyEnabled") return 1;
|
||||
@@ -167,6 +169,8 @@ class ERR_Settings
|
||||
m_Values.Set("civilianTrafficCount", AddedDefault("civilianTrafficCount"));
|
||||
m_Values.Set("startMode", AddedDefault("startMode"));
|
||||
m_Values.Set("keepWeaponOnUnconscious", AddedDefault("keepWeaponOnUnconscious"));
|
||||
m_Values.Set("restartOnEmpty", AddedDefault("restartOnEmpty"));
|
||||
m_Values.Set("emptyRestartHours", AddedDefault("emptyRestartHours"));
|
||||
m_Values.Set("reportIntervalSeconds", 10);
|
||||
m_Values.Set("supportSquadLimit", 4);
|
||||
m_Values.Set("responseRadiusMetres", 2000);
|
||||
|
||||
Reference in New Issue
Block a user