Role bans (#6703)
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user