Role ban UI updates (#16736)

This commit is contained in:
metalgearsloth
2023-05-27 14:22:22 +10:00
committed by GitHub
parent 4f678e0f25
commit be14380520
11 changed files with 255 additions and 120 deletions

View File

@@ -60,7 +60,7 @@ namespace Content.Client.Entry
[Dependency] private readonly DocumentParsingManager _documentParsingManager = default!;
[Dependency] private readonly GhostKickManager _ghostKick = default!;
[Dependency] private readonly ExtendedDisconnectInformationManager _extendedDisconnectInformation = default!;
[Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
[Dependency] private readonly ContentLocalizationManager _contentLoc = default!;
public override void Init()
@@ -130,7 +130,7 @@ namespace Content.Client.Entry
_viewportManager.Initialize();
_ghostKick.Initialize();
_extendedDisconnectInformation.Initialize();
_playTimeTracking.Initialize();
_jobRequirements.Initialize();
//AUTOSCALING default Setup!
_configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080);

View File

@@ -42,7 +42,7 @@ namespace Content.Client.IoC
IoCManager.Register<ISharedAdminLogManager, SharedAdminLogManager>();
IoCManager.Register<GhostKickManager>();
IoCManager.Register<ExtendedDisconnectInformationManager>();
IoCManager.Register<PlayTimeTrackingManager>();
IoCManager.Register<JobRequirementsManager>();
IoCManager.Register<DocumentParsingManager>();
}
}

View File

@@ -22,7 +22,7 @@ namespace Content.Client.LateJoin
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
public event Action<(EntityUid, string)> SelectedId;
@@ -54,6 +54,7 @@ namespace Content.Client.LateJoin
Contents.AddChild(_base);
_jobRequirements.Updated += RebuildUI;
RebuildUI();
SelectedId += x =>
@@ -249,7 +250,7 @@ namespace Content.Client.LateJoin
jobButton.OnPressed += _ => SelectedId.Invoke((id, jobButton.JobId));
if (!_playTimeTracking.IsAllowed(prototype, out var reason))
if (!_jobRequirements.IsAllowed(prototype, out var reason))
{
jobButton.Disabled = true;
@@ -289,6 +290,7 @@ namespace Content.Client.LateJoin
if (disposing)
{
_jobRequirements.Updated -= RebuildUI;
_gameTicker.LobbyJobsAvailableUpdated -= JobsAvailableUpdated;
_jobButtons.Clear();
_jobCategories.Clear();

View File

@@ -1,6 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Content.Shared.CCVar;
using Content.Shared.Players;
using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Roles;
using Robust.Client;
@@ -11,7 +13,7 @@ using Robust.Shared.Prototypes;
namespace Content.Client.Players.PlayTimeTracking;
public sealed class PlayTimeTrackingManager
public sealed class JobRequirementsManager
{
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IClientNetManager _net = default!;
@@ -20,9 +22,18 @@ public sealed class PlayTimeTrackingManager
[Dependency] private readonly IPrototypeManager _prototypes = default!;
private readonly Dictionary<string, TimeSpan> _roles = new();
private readonly List<string> _roleBans = new();
private ISawmill _sawmill = default!;
public event Action? Updated;
public void Initialize()
{
_sawmill = Logger.GetSawmill("job_requirements");
// Yeah the client manager handles role bans and playtime but the server ones are separate DEAL.
_net.RegisterNetMessage<MsgRoleBans>(RxRoleBans);
_net.RegisterNetMessage<MsgPlayTime>(RxPlayTime);
_client.RunLevelChanged += ClientOnRunLevelChanged;
@@ -37,6 +48,18 @@ public sealed class PlayTimeTrackingManager
}
}
private void RxRoleBans(MsgRoleBans message)
{
_sawmill.Debug($"Received roleban info containing {message.Bans.Count} entries.");
if (_roleBans.Equals(message.Bans))
return;
_roleBans.Clear();
_roleBans.AddRange(message.Bans);
Updated?.Invoke();
}
private void RxPlayTime(MsgPlayTime message)
{
_roles.Clear();
@@ -52,27 +75,36 @@ public sealed class PlayTimeTrackingManager
{
sawmill.Info($"{tracker}: {time}");
}*/
Updated?.Invoke();
}
public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out string? reason)
{
reason = null;
if (_roleBans.Contains($"Job:{job.ID}"))
{
reason = Loc.GetString("role-ban");
return false;
}
if (job.Requirements == null ||
!_cfg.GetCVar(CCVars.GameRoleTimers))
{
return true;
}
var player = _playerManager.LocalPlayer?.Session;
if (player == null) return true;
if (player == null)
return true;
var roles = _roles;
var reasonBuilder = new StringBuilder();
var first = true;
foreach (var requirement in job.Requirements)
{
if (JobRequirements.TryRequirementMet(requirement, roles, out reason, _prototypes))
if (JobRequirements.TryRequirementMet(requirement, _roles, out reason, _prototypes))
continue;
if (!first)

View File

@@ -53,6 +53,7 @@ namespace Content.Client.Preferences.UI
private readonly IEntityManager _entMan;
private readonly IConfigurationManager _configurationManager;
private readonly MarkingManager _markingManager;
private readonly JobRequirementsManager _requirements;
private LineEdit _ageEdit => CAgeEdit;
private LineEdit _nameEdit => CNameEdit;
@@ -377,96 +378,9 @@ namespace Content.Client.Preferences.UI
_jobPriorities = new List<JobPrioritySelector>();
_jobCategories = new Dictionary<string, BoxContainer>();
var firstCategory = true;
var playTime = IoCManager.Resolve<PlayTimeTrackingManager>();
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
var departmentName = Loc.GetString($"department-{department.ID}");
if (!_jobCategories.TryGetValue(department.ID, out var category))
{
category = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Name = department.ID,
ToolTip = Loc.GetString("humanoid-profile-editor-jobs-amount-in-department-tooltip",
("departmentName", departmentName))
};
if (firstCategory)
{
firstCategory = false;
}
else
{
category.AddChild(new Control
{
MinSize = new Vector2(0, 23),
});
}
category.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#464966")},
Children =
{
new Label
{
Text = Loc.GetString("humanoid-profile-editor-department-jobs-label",
("departmentName", departmentName)),
Margin = new Thickness(5f, 0, 0, 0)
}
}
});
_jobCategories[department.ID] = category;
_jobList.AddChild(category);
}
var jobs = department.Roles.Select(o => _prototypeManager.Index<JobPrototype>(o)).Where(o => o.SetPreference).ToList();
jobs.Sort((x, y) => -string.Compare(x.LocalizedName, y.LocalizedName, StringComparison.CurrentCultureIgnoreCase));
foreach (var job in jobs)
{
var selector = new JobPrioritySelector(job);
if (!playTime.IsAllowed(job, out var reason))
{
selector.LockRequirements(reason);
}
category.AddChild(selector);
_jobPriorities.Add(selector);
selector.PriorityChanged += priority =>
{
Profile = Profile?.WithJobPriority(job.ID, priority);
IsDirty = true;
foreach (var jobSelector in _jobPriorities)
{
// Sync other selectors with the same job in case of multiple department jobs
if (jobSelector.Job == selector.Job)
{
jobSelector.Priority = priority;
}
// Lower any other high priorities to medium.
if (priority == JobPriority.High)
{
if (jobSelector.Job != selector.Job && jobSelector.Priority == JobPriority.High)
{
jobSelector.Priority = JobPriority.Medium;
Profile = Profile?.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium);
}
}
}
};
}
}
_requirements = IoCManager.Resolve<JobRequirementsManager>();
_requirements.Updated += UpdateRoleRequirements;
UpdateRoleRequirements();
#endregion Jobs
@@ -603,6 +517,101 @@ namespace Content.Client.Preferences.UI
IsDirty = false;
}
private void UpdateRoleRequirements()
{
_jobList.DisposeAllChildren();
_jobPriorities.Clear();
_jobCategories.Clear();
var firstCategory = true;
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
var departmentName = Loc.GetString($"department-{department.ID}");
if (!_jobCategories.TryGetValue(department.ID, out var category))
{
category = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Name = department.ID,
ToolTip = Loc.GetString("humanoid-profile-editor-jobs-amount-in-department-tooltip",
("departmentName", departmentName))
};
if (firstCategory)
{
firstCategory = false;
}
else
{
category.AddChild(new Control
{
MinSize = new Vector2(0, 23),
});
}
category.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#464966")},
Children =
{
new Label
{
Text = Loc.GetString("humanoid-profile-editor-department-jobs-label",
("departmentName", departmentName)),
Margin = new Thickness(5f, 0, 0, 0)
}
}
});
_jobCategories[department.ID] = category;
_jobList.AddChild(category);
}
var jobs = department.Roles.Select(o => _prototypeManager.Index<JobPrototype>(o)).Where(o => o.SetPreference).ToList();
jobs.Sort((x, y) => -string.Compare(x.LocalizedName, y.LocalizedName, StringComparison.CurrentCultureIgnoreCase));
foreach (var job in jobs)
{
var selector = new JobPrioritySelector(job);
if (!_requirements.IsAllowed(job, out var reason))
{
selector.LockRequirements(reason);
}
category.AddChild(selector);
_jobPriorities.Add(selector);
selector.PriorityChanged += priority =>
{
Profile = Profile?.WithJobPriority(job.ID, priority);
IsDirty = true;
foreach (var jobSelector in _jobPriorities)
{
// Sync other selectors with the same job in case of multiple department jobs
if (jobSelector.Job == selector.Job)
{
jobSelector.Priority = priority;
}
// Lower any other high priorities to medium.
if (priority == JobPriority.High)
{
if (jobSelector.Job != selector.Job && jobSelector.Priority == JobPriority.High)
{
jobSelector.Priority = JobPriority.Medium;
Profile = Profile?.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium);
}
}
}
};
}
}
}
private void OnFlavorTextChange(string content)
{
if (Profile is null)
@@ -694,6 +703,8 @@ namespace Content.Client.Preferences.UI
if (_previewDummy != null)
_entMan.DeleteEntity(_previewDummy.Value);
var playTime = IoCManager.Resolve<JobRequirementsManager>();
playTime.Updated -= UpdateRoleRequirements;
_preferencesManager.OnServerDataLoaded -= LoadServerData;
}

View File

@@ -9,6 +9,10 @@ namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Ban)]
public sealed class DepartmentBanCommand : IConsoleCommand
{
[Dependency] private readonly IPlayerLocator _locater = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly RoleBanManager _bans = default!;
public string Command => "departmentban";
public string Description => Loc.GetString("cmd-departmentban-desc");
public string Help => Loc.GetString("cmd-departmentban-help");
@@ -46,19 +50,25 @@ public sealed class DepartmentBanCommand : IConsoleCommand
return;
}
var protoManager = IoCManager.Resolve<IPrototypeManager>();
if (!protoManager.TryIndex<DepartmentPrototype>(department, out var departmentProto))
if (!_protoManager.TryIndex<DepartmentPrototype>(department, out var departmentProto))
{
return;
}
var banManager = IoCManager.Resolve<RoleBanManager>();
var located = await _locater.LookupIdByNameOrIdAsync(target);
if (located == null)
{
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
return;
}
foreach (var job in departmentProto.Roles)
{
banManager.CreateJobBan(shell, target, job, reason, minutes);
_bans.CreateJobBan(shell, located, job, reason, minutes);
}
_bans.SendRoleBans(located);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)

View File

@@ -12,6 +12,9 @@ namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Ban)]
public sealed class RoleBanCommand : IConsoleCommand
{
[Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly RoleBanManager _bans = default!;
public string Command => "roleban";
public string Description => Loc.GetString("cmd-roleban-desc");
public string Help => Loc.GetString("cmd-roleban-help");
@@ -49,7 +52,16 @@ public sealed class RoleBanCommand : IConsoleCommand
return;
}
IoCManager.Resolve<RoleBanManager>().CreateJobBan(shell, target, job, reason, minutes);
var located = await _locator.LookupIdByNameOrIdAsync(target);
if (located == null)
{
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
return;
}
_bans.CreateJobBan(shell, located, job, reason, minutes);
_bans.SendRoleBans(located);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)

View File

@@ -5,6 +5,7 @@ using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Content.Server.Database;
using Content.Shared.Players;
using Content.Shared.Roles;
using Robust.Server.Player;
using Robust.Shared.Console;
@@ -16,17 +17,21 @@ namespace Content.Server.Administration.Managers;
public sealed class RoleBanManager
{
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
private const string JobPrefix = "Job:";
private ISawmill _sawmill = default!;
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new();
public void Initialize()
{
_sawmill = Logger.GetSawmill("rolebans");
_netManager.RegisterNetMessage<MsgRoleBans>();
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
}
@@ -34,10 +39,13 @@ public sealed class RoleBanManager
{
if (e.NewStatus != SessionStatus.Connected
|| _cachedRoleBans.ContainsKey(e.Session.UserId))
{
return;
}
var netChannel = e.Session.ConnectedClient;
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId);
SendRoleBans(e.Session);
}
private async Task<bool> AddRoleBan(ServerRoleBanDef banDef)
@@ -49,14 +57,41 @@ public sealed class RoleBanManager
roleBans = new HashSet<ServerRoleBanDef>();
_cachedRoleBans.Add(banDef.UserId.Value, roleBans);
}
if (!roleBans.Contains(banDef))
roleBans.Add(banDef);
roleBans.Add(banDef);
}
await _db.AddServerRoleBanAsync(banDef);
return true;
}
public void SendRoleBans(LocatedPlayerData located)
{
if (!_playerManager.TryGetSessionById(located.UserId, out var player))
{
return;
}
SendRoleBans(player);
}
public void SendRoleBans(IPlayerSession pSession)
{
if (!_cachedRoleBans.TryGetValue(pSession.UserId, out var roleBans))
{
_sawmill.Error($"Tried to send rolebans for {pSession.Name} but none cached?");
return;
}
var bans = new MsgRoleBans()
{
Bans = roleBans.Select(o => o.Role).ToList()
};
_sawmill.Debug($"Sent rolebans to {pSession.Name}");
_netManager.ServerSendMessage(bans, pSession.ConnectedClient);
}
public HashSet<string>? GetRoleBans(NetUserId playerUserId)
{
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null;
@@ -98,7 +133,7 @@ public sealed class RoleBanManager
}
#region Job Bans
public async void CreateJobBan(IConsoleShell shell, string target, string job, string reason, uint minutes)
public async void CreateJobBan(IConsoleShell shell, LocatedPlayerData located, string job, string reason, uint minutes)
{
if (!_prototypeManager.TryIndex(job, out JobPrototype? _))
{
@@ -107,7 +142,7 @@ public sealed class RoleBanManager
}
job = string.Concat(JobPrefix, job);
CreateRoleBan(shell, target, job, reason, minutes);
CreateRoleBan(shell, located, job, reason, minutes);
}
public HashSet<string>? GetJobBans(NetUserId playerUserId)
@@ -122,15 +157,8 @@ public sealed class RoleBanManager
#endregion
#region Commands
private async void CreateRoleBan(IConsoleShell shell, string target, string role, string reason, uint minutes)
private async void CreateRoleBan(IConsoleShell shell, LocatedPlayerData located, string role, string reason, uint minutes)
{
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
if (located == null)
{
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
return;
}
var targetUid = located.UserId;
var targetHWid = located.LastHWId;
var targetAddress = located.LastAddress;
@@ -167,12 +195,12 @@ public sealed class RoleBanManager
if (!await AddRoleBan(banDef))
{
shell.WriteLine(Loc.GetString("cmd-roleban-existing", ("target", target), ("role", role)));
shell.WriteLine(Loc.GetString("cmd-roleban-existing", ("target", located.Username), ("role", role)));
return;
}
var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires));
shell.WriteLine(Loc.GetString("cmd-roleban-success", ("target", target), ("role", role), ("reason", reason), ("length", length)));
shell.WriteLine(Loc.GetString("cmd-roleban-success", ("target", located.Username), ("role", role), ("reason", reason), ("length", length)));
}
#endregion
}

View File

@@ -0,0 +1,36 @@
using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
namespace Content.Shared.Players;
/// <summary>
/// Sent server -> client to inform the client of their role bans.
/// </summary>
public sealed class MsgRoleBans : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;
public List<string> Bans = new();
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
var count = buffer.ReadVariableInt32();
Bans.EnsureCapacity(count);
for (var i = 0; i < count; i++)
{
Bans.Add(buffer.ReadString());
}
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
buffer.WriteVariableInt32(Bans.Count);
foreach (var ban in Bans)
{
buffer.Write(ban);
}
}
}

View File

@@ -16,6 +16,8 @@ public sealed class MsgPlayTime : NetMessage
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
var count = buffer.ReadVariableInt32();
Trackers.EnsureCapacity(count);
for (var i = 0; i < count; i++)
{
Trackers.Add(buffer.ReadString(), buffer.ReadTimeSpan());

View File

@@ -6,3 +6,5 @@ role-timer-role-insufficient = You require {TOSTRING($time, "0")} more minutes w
role-timer-role-too-high = You require {TOSTRING($time, "0")} fewer minutes with {$job} to play this role. (Are you trying to play a trainee role?)
role-timer-locked = Locked (hover for details)
role-ban = You have been banned from this role.