This commit is contained in:
ShadowCommander
2022-02-21 14:11:39 -08:00
committed by GitHub
parent fd8d9d2953
commit 4825142210
25 changed files with 3429 additions and 130 deletions

View File

@@ -7,6 +7,7 @@ using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.MobState.Components;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -18,6 +19,56 @@ namespace Content.Server.GameTicking
private GamePresetPrototype? _preset;
private bool StartPreset(IPlayerSession[] origReadyPlayers, bool force)
{
var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
RaiseLocalEvent(startAttempt);
if (!startAttempt.Cancelled)
return true;
var presetTitle = _preset != null ? Loc.GetString(_preset.ModeTitle) : string.Empty;
void FailedPresetRestart()
{
SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart",
("failedGameMode", presetTitle)));
RestartRound();
DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
}
if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled))
{
var oldPreset = _preset;
ClearGameRules();
SetGamePreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset));
AddGamePresetRules();
StartGamePresetRules();
startAttempt.Uncancel();
RaiseLocalEvent(startAttempt);
_chatManager.DispatchServerAnnouncement(
Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback",
("failedGameMode", presetTitle),
("fallbackMode", Loc.GetString(_preset!.ModeTitle))));
if (startAttempt.Cancelled)
{
FailedPresetRestart();
}
RefreshLateJoinAllowed();
}
else
{
FailedPresetRestart();
return false;
}
return true;
}
private void InitializeGamePreset()
{
SetGamePreset(_configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset));

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -7,12 +6,10 @@ using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Station;
using Robust.Server.Player;
using Robust.Shared.Localization;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameTicking
{
@@ -25,17 +22,18 @@ namespace Content.Server.GameTicking
[ViewVariables]
private readonly Dictionary<string, int> _spawnedPositions = new();
private Dictionary<IPlayerSession, (string, StationId)> AssignJobs(List<IPlayerSession> available,
private Dictionary<IPlayerSession, (string, StationId)> AssignJobs(List<IPlayerSession> availablePlayers,
Dictionary<NetUserId, HumanoidCharacterProfile> profiles)
{
var assigned = new Dictionary<IPlayerSession, (string, StationId)>();
List<(IPlayerSession, List<string>)> GetPlayersJobCandidates(bool heads, JobPriority i)
{
return available.Select(player =>
return availablePlayers.Select(player =>
{
var profile = profiles[player.UserId];
var roleBans = _roleBanManager.GetJobBans(player.UserId);
var availableJobs = profile.JobPriorities
.Where(j =>
{
@@ -53,6 +51,7 @@ namespace Content.Server.GameTicking
return priority == i;
})
.Where(p => roleBans != null && !roleBans.Contains(p.Key))
.Select(j => j.Key)
.ToList();
@@ -85,7 +84,7 @@ namespace Content.Server.GameTicking
}
}
available.RemoveAll(a => assigned.ContainsKey(a));
availablePlayers.RemoveAll(a => assigned.ContainsKey(a));
}
// Current strategy is to fill each station one by one.
@@ -106,14 +105,17 @@ namespace Content.Server.GameTicking
return assigned;
}
private string? PickBestAvailableJob(HumanoidCharacterProfile profile, StationId station)
private string? PickBestAvailableJob(IPlayerSession playerSession, HumanoidCharacterProfile profile,
StationId station)
{
var available = _stationSystem.StationInfo[station].JobList;
bool TryPick(JobPriority priority, [NotNullWhen(true)] out string? jobId)
{
var roleBans = _roleBanManager.GetJobBans(playerSession.UserId);
var filtered = profile.JobPriorities
.Where(p => p.Value == priority)
.Where(p => roleBans != null && !roleBans.Contains(p.Key))
.Select(p => p.Key)
.ToList();

View File

@@ -69,7 +69,7 @@ namespace Content.Server.GameTicking
[ViewVariables]
public int RoundId { get; private set; }
private void PreRoundSetup()
private void LoadMaps()
{
AddGamePresetRules();
@@ -192,6 +192,14 @@ namespace Content.Server.GameTicking
StartGamePresetRules();
RoundLengthMetric.Set(0);
var playerIds = _playersInLobby.Keys.Select(player => player.UserId.UserId).ToArray();
RoundId = await _db.AddNewRound(playerIds);
var startingEvent = new RoundStartingEvent();
RaiseLocalEvent(startingEvent);
List<IPlayerSession> readyPlayers;
if (LobbyEnabled)
{
@@ -203,13 +211,13 @@ namespace Content.Server.GameTicking
readyPlayers = _playersInLobby.Keys.ToList();
}
RoundLengthMetric.Set(0);
var playerIds = _playersInLobby.Keys.Select(player => player.UserId.UserId).ToArray();
RoundId = await _db.AddNewRound(playerIds);
var startingEvent = new RoundStartingEvent();
RaiseLocalEvent(startingEvent);
readyPlayers.RemoveAll(p =>
{
if (_roleBanManager.GetRoleBans(p.UserId) != null)
return false;
Logger.ErrorS("RoleBans", $"Role bans for player {p} {p.UserId} have not been loaded yet.");
return true;
});
// Get the profiles for each player for easier lookup.
var profiles = _prefsManager.GetSelectedProfilesForPlayers(
@@ -227,106 +235,13 @@ namespace Content.Server.GameTicking
var origReadyPlayers = readyPlayers.ToArray();
var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
RaiseLocalEvent(startAttempt);
var presetTitle = _preset != null ? Loc.GetString(_preset.ModeTitle) : string.Empty;
void FailedPresetRestart()
{
SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart",
("failedGameMode", presetTitle)));
RestartRound();
DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
}
if (startAttempt.Cancelled)
{
if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled))
{
var oldPreset = _preset;
ClearGameRules();
SetGamePreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset));
AddGamePresetRules();
StartGamePresetRules();
startAttempt.Uncancel();
RaiseLocalEvent(startAttempt);
_chatManager.DispatchServerAnnouncement(
Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback",
("failedGameMode", presetTitle),
("fallbackMode", Loc.GetString(_preset!.ModeTitle))));
if (startAttempt.Cancelled)
{
FailedPresetRestart();
}
RefreshLateJoinAllowed();
}
else
{
FailedPresetRestart();
return;
}
}
if (!StartPreset(origReadyPlayers, force))
return;
// MapInitialize *before* spawning players, our codebase is too shit to do it afterwards...
_mapManager.DoMapInitialize(DefaultMap);
// Allow game rules to spawn players by themselves if needed. (For example, nuke ops or wizard)
RaiseLocalEvent(new RulePlayerSpawningEvent(readyPlayers, profiles, force));
var assignedJobs = AssignJobs(readyPlayers, profiles);
// For players without jobs, give them the overflow job if they have that set...
foreach (var player in origReadyPlayers)
{
if (assignedJobs.ContainsKey(player))
{
continue;
}
var profile = profiles[player.UserId];
if (profile.PreferenceUnavailable == PreferenceUnavailableMode.SpawnAsOverflow)
{
// Pick a random station
var stations = _stationSystem.StationInfo.Keys.ToList();
_robustRandom.Shuffle(stations);
if (stations.Count == 0)
{
assignedJobs.Add(player, (FallbackOverflowJob, StationId.Invalid));
continue;
}
foreach (var station in stations)
{
// Pick a random overflow job from that station
var overflows = _stationSystem.StationInfo[station].MapPrototype.OverflowJobs.Clone();
_robustRandom.Shuffle(overflows);
// Stations with no overflow slots should simply get skipped over.
if (overflows.Count == 0)
continue;
// If the overflow exists, put them in as it.
assignedJobs.Add(player, (overflows[0], stations[0]));
}
}
}
// Spawn everybody in!
foreach (var (player, (job, station)) in assignedJobs)
{
SpawnPlayer(player, profiles[player.UserId], station, job, false);
}
RefreshLateJoinAllowed();
// Allow rules to add roles to players who have been spawned in. (For example, on-station traitors)
RaiseLocalEvent(new RulePlayerJobsAssignedEvent(assignedJobs.Keys.ToArray(), profiles, force));
SpawnPlayers(readyPlayers, origReadyPlayers, profiles, force);
_roundStartDateTime = DateTime.UtcNow;
RunLevel = GameRunLevel.InRound;
@@ -458,7 +373,7 @@ namespace Content.Server.GameTicking
RunLevel = GameRunLevel.PreRoundLobby;
LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
ResettingCleanup();
PreRoundSetup();
LoadMaps();
if (!LobbyEnabled)
{
@@ -508,6 +423,8 @@ namespace Content.Server.GameTicking
_mapManager.Restart();
_roleBanManager.Restart();
// Clear up any game rules.
ClearGameRules();

View File

@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Content.Server.Access.Systems;
@@ -22,13 +20,10 @@ using Content.Shared.Roles;
using Content.Shared.Species;
using Content.Shared.Station;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameTicking
{
@@ -48,6 +43,71 @@ namespace Content.Server.GameTicking
// Mainly to avoid allocations.
private readonly List<EntityCoordinates> _possiblePositions = new();
private void SpawnPlayers(List<IPlayerSession> readyPlayers, IPlayerSession[] origReadyPlayers,
Dictionary<NetUserId, HumanoidCharacterProfile> profiles, bool force)
{
// Allow game rules to spawn players by themselves if needed. (For example, nuke ops or wizard)
RaiseLocalEvent(new RulePlayerSpawningEvent(readyPlayers, profiles, force));
var assignedJobs = AssignJobs(readyPlayers, profiles);
AssignOverflowJobs(assignedJobs, origReadyPlayers, profiles);
// Spawn everybody in!
foreach (var (player, (job, station)) in assignedJobs)
{
SpawnPlayer(player, profiles[player.UserId], station, job, false);
}
RefreshLateJoinAllowed();
// Allow rules to add roles to players who have been spawned in. (For example, on-station traitors)
RaiseLocalEvent(new RulePlayerJobsAssignedEvent(assignedJobs.Keys.ToArray(), profiles, force));
}
private void AssignOverflowJobs(IDictionary<IPlayerSession, (string, StationId)> assignedJobs,
IPlayerSession[] origReadyPlayers, IReadOnlyDictionary<NetUserId, HumanoidCharacterProfile> profiles)
{
// For players without jobs, give them the overflow job if they have that set...
foreach (var player in origReadyPlayers)
{
if (assignedJobs.ContainsKey(player))
{
continue;
}
var profile = profiles[player.UserId];
if (profile.PreferenceUnavailable != PreferenceUnavailableMode.SpawnAsOverflow)
continue;
// Pick a random station
var stations = _stationSystem.StationInfo.Keys.ToList();
if (stations.Count == 0)
{
assignedJobs.Add(player, (FallbackOverflowJob, StationId.Invalid));
continue;
}
_robustRandom.Shuffle(stations);
foreach (var station in stations)
{
// Pick a random overflow job from that station
var overflows = _stationSystem.StationInfo[station].MapPrototype.OverflowJobs.Clone();
_robustRandom.Shuffle(overflows);
// Stations with no overflow slots should simply get skipped over.
if (overflows.Count == 0)
continue;
// If the overflow exists, put them in as it.
assignedJobs.Add(player, (overflows[0], stations[0]));
break;
}
}
}
private void SpawnPlayer(IPlayerSession player, StationId station, string? jobId = null, bool lateJoin = true)
{
var character = GetPlayerProfile(player);
@@ -90,7 +150,7 @@ namespace Content.Server.GameTicking
}
// Pick best job best on prefs.
jobId ??= PickBestAvailableJob(character, station);
jobId ??= PickBestAvailableJob(player, character, station);
// If no job available, stay in lobby, or if no lobby spawn as observer
if (jobId is null)
{

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Server.CharacterAppearance.Systems;
using Content.Server.Chat.Managers;
using Content.Server.Ghost;
@@ -16,14 +17,12 @@ using Robust.Shared.Configuration;
#if EXCEPTION_TOLERANCE
using Robust.Shared.Exceptions;
#endif
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameTicking
{
@@ -99,5 +98,6 @@ namespace Content.Server.GameTicking
[Dependency] private readonly PDASystem _pdaSystem = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly GhostSystem _ghosts = default!;
[Dependency] private readonly RoleBanManager _roleBanManager = default!;
}
}