Fixes client having authority over rules popup cvars (#28655)
* Fixes client having authority over rules popup cvars * Delete duplicate migration * Pre-update * Post-update
This commit is contained in:
@@ -1,13 +1,9 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Content.Client.Gameplay;
|
using Content.Client.Gameplay;
|
||||||
using Content.Client.Info;
|
using Content.Client.Info;
|
||||||
using Content.Shared.Administration.Managers;
|
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Guidebook;
|
using Content.Shared.Guidebook;
|
||||||
using Content.Shared.Info;
|
using Content.Shared.Info;
|
||||||
using Robust.Client;
|
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -18,24 +14,20 @@ namespace Content.Client.UserInterface.Systems.Info;
|
|||||||
|
|
||||||
public sealed class InfoUIController : UIController, IOnStateExited<GameplayState>
|
public sealed class InfoUIController : UIController, IOnStateExited<GameplayState>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IBaseClient _client = default!;
|
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
[Dependency] private readonly ISharedAdminManager _adminManager = default!;
|
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
|
|
||||||
private RulesPopup? _rulesPopup;
|
private RulesPopup? _rulesPopup;
|
||||||
private RulesAndInfoWindow? _infoWindow;
|
private RulesAndInfoWindow? _infoWindow;
|
||||||
|
|
||||||
private static DateTime NextRulesReadTime => DateTime.UtcNow + TimeSpan.FromDays(60);
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
_client.PlayerJoinedServer += OnJoinedServer;
|
|
||||||
|
_netManager.RegisterNetMessage<RulesAcceptedMessage>();
|
||||||
_netManager.RegisterNetMessage<ShowRulesPopupMessage>(OnShowRulesPopupMessage);
|
_netManager.RegisterNetMessage<ShowRulesPopupMessage>(OnShowRulesPopupMessage);
|
||||||
|
|
||||||
_consoleHost.RegisterCommand("fuckrules",
|
_consoleHost.RegisterCommand("fuckrules",
|
||||||
@@ -47,22 +39,6 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnJoinedServer(object? sender, PlayerEventArgs args)
|
|
||||||
{
|
|
||||||
if (_playerManager.LocalSession is not { } localSession)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_adminManager.IsAdmin(localSession) && _cfg.GetCVar(CCVars.RulesExemptLocal))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var nextReadTarget = DateTime.Parse(_cfg.GetCVar(CCVars.RulesNextPopupTime));
|
|
||||||
if (nextReadTarget >= DateTime.UtcNow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var time = _cfg.GetCVar(CCVars.RulesWaitTime);
|
|
||||||
ShowRules(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnShowRulesPopupMessage(ShowRulesPopupMessage message)
|
private void OnShowRulesPopupMessage(ShowRulesPopupMessage message)
|
||||||
{
|
{
|
||||||
ShowRules(message.PopupTime);
|
ShowRules(message.PopupTime);
|
||||||
@@ -100,8 +76,7 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
|
|||||||
|
|
||||||
private void OnAcceptPressed()
|
private void OnAcceptPressed()
|
||||||
{
|
{
|
||||||
_cfg.SetCVar(CCVars.RulesNextPopupTime, NextRulesReadTime.ToString(CultureInfo.InvariantCulture));
|
_netManager.ClientSendMessage(new RulesAcceptedMessage());
|
||||||
_cfg.SaveToFile();
|
|
||||||
|
|
||||||
_rulesPopup?.Orphan();
|
_rulesPopup?.Orphan();
|
||||||
_rulesPopup = null;
|
_rulesPopup = null;
|
||||||
|
|||||||
1913
Content.Server.Database/Migrations/Postgres/20240606175154_ReturnLastReadRules.Designer.cs
generated
Normal file
1913
Content.Server.Database/Migrations/Postgres/20240606175154_ReturnLastReadRules.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Postgres
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ReturnLastReadRules : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "last_read_rules",
|
||||||
|
table: "player",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "last_read_rules",
|
||||||
|
table: "player");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -648,6 +648,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("first_seen_time");
|
.HasColumnName("first_seen_time");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastReadRules")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_read_rules");
|
||||||
|
|
||||||
b.Property<IPAddress>("LastSeenAddress")
|
b.Property<IPAddress>("LastSeenAddress")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("inet")
|
.HasColumnType("inet")
|
||||||
|
|||||||
1838
Content.Server.Database/Migrations/Sqlite/20240606175141_ReturnLastReadRules.Designer.cs
generated
Normal file
1838
Content.Server.Database/Migrations/Sqlite/20240606175141_ReturnLastReadRules.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ReturnLastReadRules : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "last_read_rules",
|
||||||
|
table: "player",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "last_read_rules",
|
||||||
|
table: "player");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -608,6 +608,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("first_seen_time");
|
.HasColumnName("first_seen_time");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastReadRules")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last_read_rules");
|
||||||
|
|
||||||
b.Property<string>("LastSeenAddress")
|
b.Property<string>("LastSeenAddress")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
|
|||||||
@@ -520,6 +520,8 @@ namespace Content.Server.Database
|
|||||||
public List<Round> Rounds { get; set; } = null!;
|
public List<Round> Rounds { get; set; } = null!;
|
||||||
public List<AdminLogPlayer> AdminLogs { get; set; } = null!;
|
public List<AdminLogPlayer> AdminLogs { get; set; } = null!;
|
||||||
|
|
||||||
|
public DateTime? LastReadRules { get; set; }
|
||||||
|
|
||||||
public List<AdminNote> AdminNotesReceived { get; set; } = null!;
|
public List<AdminNote> AdminNotesReceived { get; set; } = null!;
|
||||||
public List<AdminNote> AdminNotesCreated { get; set; } = null!;
|
public List<AdminNote> AdminNotesCreated { get; set; } = null!;
|
||||||
public List<AdminNote> AdminNotesLastEdited { get; set; } = null!;
|
public List<AdminNote> AdminNotesLastEdited { get; set; } = null!;
|
||||||
|
|||||||
@@ -240,7 +240,6 @@ namespace Content.Server.Administration.Managers
|
|||||||
_sawmill = _logManager.GetSawmill("admin");
|
_sawmill = _logManager.GetSawmill("admin");
|
||||||
|
|
||||||
_netMgr.RegisterNetMessage<MsgUpdateAdminStatus>();
|
_netMgr.RegisterNetMessage<MsgUpdateAdminStatus>();
|
||||||
_netMgr.RegisterNetMessage<ShowRulesPopupMessage>();
|
|
||||||
|
|
||||||
// Cache permissions for loaded console commands with the requisite attributes.
|
// Cache permissions for loaded console commands with the requisite attributes.
|
||||||
foreach (var (cmdName, cmd) in _consoleHost.AvailableCommands)
|
foreach (var (cmdName, cmd) in _consoleHost.AvailableCommands)
|
||||||
|
|||||||
@@ -1032,6 +1032,30 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
|||||||
await db.DbContext.SaveChangesAsync();
|
await db.DbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<DateTimeOffset?> GetLastReadRules(NetUserId player)
|
||||||
|
{
|
||||||
|
await using var db = await GetDb();
|
||||||
|
|
||||||
|
return NormalizeDatabaseTime(await db.DbContext.Player
|
||||||
|
.Where(dbPlayer => dbPlayer.UserId == player)
|
||||||
|
.Select(dbPlayer => dbPlayer.LastReadRules)
|
||||||
|
.SingleOrDefaultAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetLastReadRules(NetUserId player, DateTimeOffset date)
|
||||||
|
{
|
||||||
|
await using var db = await GetDb();
|
||||||
|
|
||||||
|
var dbPlayer = await db.DbContext.Player.Where(dbPlayer => dbPlayer.UserId == player).SingleOrDefaultAsync();
|
||||||
|
if (dbPlayer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbPlayer.LastReadRules = date.UtcDateTime;
|
||||||
|
await db.DbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Uploaded Resources Logs
|
#region Uploaded Resources Logs
|
||||||
|
|||||||
@@ -252,6 +252,13 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Rules
|
||||||
|
|
||||||
|
Task<DateTimeOffset?> GetLastReadRules(NetUserId player);
|
||||||
|
Task SetLastReadRules(NetUserId player, DateTimeOffset time);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Admin Notes
|
#region Admin Notes
|
||||||
|
|
||||||
Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTimeOffset createdAt, DateTimeOffset? expiryTime);
|
Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTimeOffset createdAt, DateTimeOffset? expiryTime);
|
||||||
@@ -700,6 +707,18 @@ namespace Content.Server.Database
|
|||||||
return RunDbCommand(() => _db.PurgeUploadedResourceLogAsync(days));
|
return RunDbCommand(() => _db.PurgeUploadedResourceLogAsync(days));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<DateTimeOffset?> GetLastReadRules(NetUserId player)
|
||||||
|
{
|
||||||
|
DbReadOpsMetric.Inc();
|
||||||
|
return RunDbCommand(() => _db.GetLastReadRules(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetLastReadRules(NetUserId player, DateTimeOffset time)
|
||||||
|
{
|
||||||
|
DbWriteOpsMetric.Inc();
|
||||||
|
return RunDbCommand(() => _db.SetLastReadRules(player, time));
|
||||||
|
}
|
||||||
|
|
||||||
public Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTimeOffset createdAt, DateTimeOffset? expiryTime)
|
public Task<int> AddAdminNote(int? roundId, Guid player, TimeSpan playtimeAtNote, string message, NoteSeverity severity, bool secret, Guid createdBy, DateTimeOffset createdAt, DateTimeOffset? expiryTime)
|
||||||
{
|
{
|
||||||
DbWriteOpsMetric.Inc();
|
DbWriteOpsMetric.Inc();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Content.Server.EUI;
|
|||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.GhostKick;
|
using Content.Server.GhostKick;
|
||||||
using Content.Server.GuideGenerator;
|
using Content.Server.GuideGenerator;
|
||||||
|
using Content.Server.Info;
|
||||||
using Content.Server.IoC;
|
using Content.Server.IoC;
|
||||||
using Content.Server.Maps;
|
using Content.Server.Maps;
|
||||||
using Content.Server.NodeContainer.NodeGroups;
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
@@ -135,6 +136,7 @@ namespace Content.Server.Entry
|
|||||||
IoCManager.Resolve<RecipeManager>().Initialize();
|
IoCManager.Resolve<RecipeManager>().Initialize();
|
||||||
IoCManager.Resolve<IAdminManager>().Initialize();
|
IoCManager.Resolve<IAdminManager>().Initialize();
|
||||||
IoCManager.Resolve<IAfkManager>().Initialize();
|
IoCManager.Resolve<IAfkManager>().Initialize();
|
||||||
|
IoCManager.Resolve<RulesManager>().Initialize();
|
||||||
_euiManager.Initialize();
|
_euiManager.Initialize();
|
||||||
|
|
||||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||||
|
|||||||
44
Content.Server/Info/RulesManager.cs
Normal file
44
Content.Server/Info/RulesManager.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Content.Server.Database;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.Info;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
|
namespace Content.Server.Info;
|
||||||
|
|
||||||
|
public sealed class RulesManager
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||||
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
|
||||||
|
private static DateTime LastValidReadTime => DateTime.UtcNow - TimeSpan.FromDays(60);
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_netManager.Connected += OnConnected;
|
||||||
|
_netManager.RegisterNetMessage<ShowRulesPopupMessage>();
|
||||||
|
_netManager.RegisterNetMessage<RulesAcceptedMessage>(OnRulesAccepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnConnected(object? sender, NetChannelArgs e)
|
||||||
|
{
|
||||||
|
if (IPAddress.IsLoopback(e.Channel.RemoteEndPoint.Address) && _cfg.GetCVar(CCVars.RulesExemptLocal))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lastRead = await _dbManager.GetLastReadRules(e.Channel.UserId);
|
||||||
|
if (lastRead > LastValidReadTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = new ShowRulesPopupMessage();
|
||||||
|
message.PopupTime = _cfg.GetCVar(CCVars.RulesWaitTime);
|
||||||
|
_netManager.ServerSendMessage(message, e.Channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnRulesAccepted(RulesAcceptedMessage message)
|
||||||
|
{
|
||||||
|
var date = DateTime.UtcNow;
|
||||||
|
await _dbManager.SetLastReadRules(message.MsgChannel.UserId, date);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ using Content.Server.Database;
|
|||||||
using Content.Server.Discord;
|
using Content.Server.Discord;
|
||||||
using Content.Server.EUI;
|
using Content.Server.EUI;
|
||||||
using Content.Server.GhostKick;
|
using Content.Server.GhostKick;
|
||||||
|
using Content.Server.Info;
|
||||||
using Content.Server.Maps;
|
using Content.Server.Maps;
|
||||||
using Content.Server.MoMMI;
|
using Content.Server.MoMMI;
|
||||||
using Content.Server.NodeContainer.NodeGroups;
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
@@ -46,6 +47,7 @@ namespace Content.Server.IoC
|
|||||||
IoCManager.Register<IPlayerLocator, PlayerLocator>();
|
IoCManager.Register<IPlayerLocator, PlayerLocator>();
|
||||||
IoCManager.Register<IAfkManager, AfkManager>();
|
IoCManager.Register<IAfkManager, AfkManager>();
|
||||||
IoCManager.Register<IGameMapManager, GameMapManager>();
|
IoCManager.Register<IGameMapManager, GameMapManager>();
|
||||||
|
IoCManager.Register<RulesManager, RulesManager>();
|
||||||
IoCManager.Register<IBanManager, BanManager>();
|
IoCManager.Register<IBanManager, BanManager>();
|
||||||
IoCManager.Register<ContentNetworkResourceManager>();
|
IoCManager.Register<ContentNetworkResourceManager>();
|
||||||
IoCManager.Register<IAdminNotesManager, AdminNotesManager>();
|
IoCManager.Register<IAdminNotesManager, AdminNotesManager>();
|
||||||
|
|||||||
@@ -1796,13 +1796,7 @@ namespace Content.Shared.CCVar
|
|||||||
/// Don't show rules to localhost/loopback interface.
|
/// Don't show rules to localhost/loopback interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly CVarDef<bool> RulesExemptLocal =
|
public static readonly CVarDef<bool> RulesExemptLocal =
|
||||||
CVarDef.Create("rules.exempt_local", true, CVar.CLIENT);
|
CVarDef.Create("rules.exempt_local", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The next time the rules will popup for this client, expressed in minutes
|
|
||||||
/// </summary>
|
|
||||||
public static readonly CVarDef<string> RulesNextPopupTime =
|
|
||||||
CVarDef.Create("rules.next_popup_time", "Jan 1, 1997", CVar.CLIENTONLY | CVar.ARCHIVE);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -23,3 +23,19 @@ public sealed class ShowRulesPopupMessage : NetMessage
|
|||||||
buffer.Write(PopupTime);
|
buffer.Write(PopupTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sent by the client when it has accepted the rules.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RulesAcceptedMessage : NetMessage
|
||||||
|
{
|
||||||
|
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||||
|
|
||||||
|
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user