Disable chat for the first 5s (configurable) after joining a server.

This commit is contained in:
Paul Chote
2021-09-20 22:34:42 +01:00
committed by abcdefg30
parent 9eab92e90a
commit 8588af1001
9 changed files with 99 additions and 13 deletions

View File

@@ -34,6 +34,21 @@ namespace OpenRA.Network
TextNotificationsManager.AddSystemLine(order.TargetString); TextNotificationsManager.AddSystemLine(order.TargetString);
break; break;
case "DisableChatEntry":
{
// Order must originate from the server
if (clientId != 0)
break;
// Server may send MaxValue to indicate that it is disabled until further notice
if (order.ExtraData == uint.MaxValue)
TextNotificationsManager.ChatDisabledUntil = uint.MaxValue;
else
TextNotificationsManager.ChatDisabledUntil = Game.RunTime + order.ExtraData;
break;
}
case "Chat": case "Chat":
{ {
var client = orderManager.LobbyInfo.ClientWithIndex(clientId); var client = orderManager.LobbyInfo.ClientWithIndex(clientId);

View File

@@ -31,6 +31,7 @@ namespace OpenRA.Server
public readonly int PlayerIndex; public readonly int PlayerIndex;
public readonly string AuthToken; public readonly string AuthToken;
public readonly EndPoint EndPoint; public readonly EndPoint EndPoint;
public readonly Stopwatch ConnectionTimer = Stopwatch.StartNew();
public long TimeSinceLastResponse => Game.RunTime - lastReceivedTime; public long TimeSinceLastResponse => Game.RunTime - lastReceivedTime;

View File

@@ -476,6 +476,10 @@ namespace OpenRA.Server
LobbyInfo.Clients.Add(client); LobbyInfo.Clients.Add(client);
newConn.Validated = true; newConn.Validated = true;
// Disable chat UI to stop the client sending messages that we know we will reject
if (!client.IsAdmin && Settings.JoinChatDelay > 0)
DispatchOrdersToClient(newConn, 0, 0, new Order("DisableChatEntry", null, false) { ExtraData = (uint)Settings.JoinChatDelay }.Serialize());
Log.Write("server", "Client {0}: Accepted connection from {1}.", newConn.PlayerIndex, newConn.EndPoint); Log.Write("server", "Client {0}: Accepted connection from {1}.", newConn.PlayerIndex, newConn.EndPoint);
if (client.Fingerprint != null) if (client.Fingerprint != null)
@@ -892,8 +896,19 @@ namespace OpenRA.Server
} }
case "Chat": case "Chat":
DispatchOrdersToClients(conn, 0, o.Serialize()); {
break; var isAdmin = GetClient(conn)?.IsAdmin ?? false;
var connected = conn.ConnectionTimer.ElapsedMilliseconds;
if (!isAdmin && connected < Settings.JoinChatDelay)
{
var remaining = (Settings.JoinChatDelay - connected + 999) / 1000;
SendOrderTo(conn, "Message", "Chat is disabled. Please try again in {0} seconds".F(remaining));
}
else
DispatchOrdersToClients(conn, 0, o.Serialize());
break;
}
case "GameSaveTraitData": case "GameSaveTraitData":
{ {

View File

@@ -101,6 +101,9 @@ namespace OpenRA
[Desc("For dedicated servers only, treat maps that fail the lint checks as invalid.")] [Desc("For dedicated servers only, treat maps that fail the lint checks as invalid.")]
public bool EnableLintChecks = true; public bool EnableLintChecks = true;
[Desc("Delay in milliseconds before newly joined players can send chat messages.")]
public int JoinChatDelay = 5000;
public ServerSettings Clone() public ServerSettings Clone()
{ {
return (ServerSettings)MemberwiseClone(); return (ServerSettings)MemberwiseClone();

View File

@@ -20,6 +20,8 @@ namespace OpenRA
static Color chatMessageColor = Color.White; static Color chatMessageColor = Color.White;
static string systemMessageLabel; static string systemMessageLabel;
public static long ChatDisabledUntil { get; internal set; }
static TextNotificationsManager() static TextNotificationsManager()
{ {
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor); ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);

View File

@@ -34,8 +34,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ScrollPanelWidget chatScrollPanel; readonly ScrollPanelWidget chatScrollPanel;
readonly ContainerWidget chatTemplate; readonly ContainerWidget chatTemplate;
readonly TextFieldWidget chatText; readonly TextFieldWidget chatText;
readonly CachedTransform<int, string> chatDisabledLabel;
readonly INotifyChat[] chatTraits;
readonly TabCompletionLogic tabCompletion = new TabCompletionLogic(); readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
@@ -43,6 +42,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
TextNotification lastLine; TextNotification lastLine;
int repetitions; int repetitions;
bool chatEnabled = true;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public IngameChatLogic(Widget widget, OrderManager orderManager, World world, ModData modData, bool isMenuChat, Dictionary<string, MiniYaml> logicArgs) public IngameChatLogic(Widget widget, OrderManager orderManager, World world, ModData modData, bool isMenuChat, Dictionary<string, MiniYaml> logicArgs)
@@ -50,8 +50,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
this.orderManager = orderManager; this.orderManager = orderManager;
modRules = modData.DefaultRules; modRules = modData.DefaultRules;
chatTraits = world.WorldActor.TraitsImplementing<INotifyChat>().ToArray(); var chatTraits = world.WorldActor.TraitsImplementing<INotifyChat>().ToArray();
var players = world.Players.Where(p => p != world.LocalPlayer && !p.NonCombatant && !p.IsBot); var players = world.Players.Where(p => p != world.LocalPlayer && !p.NonCombatant && !p.IsBot);
var isObserver = orderManager.LocalClient != null && orderManager.LocalClient.IsObserver; var isObserver = orderManager.LocalClient != null && orderManager.LocalClient.IsObserver;
var alwaysDisabled = world.IsReplay || world.LobbyInfo.NonBotClients.Count() == 1; var alwaysDisabled = world.IsReplay || world.LobbyInfo.NonBotClients.Count() == 1;
@@ -82,7 +81,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
chatMode.IsDisabled = () => chatMode.IsDisabled = () =>
{ {
if (world.IsGameOver) if (world.IsGameOver || !chatEnabled)
return true; return true;
// The game is over for us, join spectator team chat // The game is over for us, join spectator team chat
@@ -100,7 +99,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}; };
} }
else else
chatMode.IsDisabled = () => disableTeamChat; chatMode.IsDisabled = () => disableTeamChat || !chatEnabled;
// Disable team chat after the game ended // Disable team chat after the game ended
world.GameOver += () => disableTeamChat = true; world.GameOver += () => disableTeamChat = true;
@@ -163,6 +162,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return true; return true;
}; };
chatDisabledLabel = new CachedTransform<int, string>(x => x > 0 ? $"Chat available in {x} seconds..." : "Chat Disabled");
if (!isMenuChat) if (!isMenuChat)
{ {
var openTeamChatKey = new HotkeyReference(); var openTeamChatKey = new HotkeyReference();
@@ -203,7 +204,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
orderManager.AddTextNotification += AddChatLineWrapper; orderManager.AddTextNotification += AddChatLineWrapper;
chatText.IsDisabled = () => world.IsReplay && !Game.Settings.Debug.EnableDebugCommandsInReplays; chatText.IsDisabled = () => !chatEnabled || (world.IsReplay && !Game.Settings.Debug.EnableDebugCommandsInReplays);
if (!isMenuChat) if (!isMenuChat)
{ {
@@ -317,6 +318,27 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Game.Sound.PlayNotification(modRules, null, "Sounds", chatLineSound, null); Game.Sound.PlayNotification(modRules, null, "Sounds", chatLineSound, null);
} }
public override void Tick()
{
var chatWasEnabled = chatEnabled;
chatEnabled = Game.RunTime >= TextNotificationsManager.ChatDisabledUntil && TextNotificationsManager.ChatDisabledUntil != uint.MaxValue;
if (chatEnabled && !chatWasEnabled)
{
chatText.Text = "";
if (Ui.KeyboardFocusWidget == null)
chatText.TakeKeyboardFocus();
}
else if (!chatEnabled)
{
var remaining = 0;
if (TextNotificationsManager.ChatDisabledUntil != uint.MaxValue)
remaining = (int)(TextNotificationsManager.ChatDisabledUntil - Game.RunTime + 999) / 1000;
chatText.Text = chatDisabledLabel.Update(remaining);
}
}
bool disposed = false; bool disposed = false;
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {

View File

@@ -48,6 +48,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ScrollPanelWidget lobbyChatPanel; readonly ScrollPanelWidget lobbyChatPanel;
readonly Widget chatTemplate; readonly Widget chatTemplate;
readonly TextFieldWidget chatTextField;
readonly CachedTransform<int, string> chatDisabledLabel;
readonly ScrollPanelWidget players; readonly ScrollPanelWidget players;
@@ -60,6 +62,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
MapPreview map; MapPreview map;
Session.MapStatus mapStatus; Session.MapStatus mapStatus;
bool chatEnabled = true;
bool addBotOnMapLoad; bool addBotOnMapLoad;
bool disableTeamChat; bool disableTeamChat;
bool insufficientPlayerSpawns; bool insufficientPlayerSpawns;
@@ -400,12 +403,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var chatMode = lobby.Get<ButtonWidget>("CHAT_MODE"); var chatMode = lobby.Get<ButtonWidget>("CHAT_MODE");
chatMode.GetText = () => teamChat ? "Team" : "All"; chatMode.GetText = () => teamChat ? "Team" : "All";
chatMode.OnClick = () => teamChat ^= true; chatMode.OnClick = () => teamChat ^= true;
chatMode.IsDisabled = () => disableTeamChat; chatMode.IsDisabled = () => disableTeamChat || !chatEnabled;
var chatTextField = lobby.Get<TextFieldWidget>("CHAT_TEXTFIELD"); chatTextField = lobby.Get<TextFieldWidget>("CHAT_TEXTFIELD");
chatTextField.IsDisabled = () => !chatEnabled;
chatTextField.MaxLength = UnitOrders.ChatMessageMaxLength; chatTextField.MaxLength = UnitOrders.ChatMessageMaxLength;
chatTextField.TakeKeyboardFocus();
chatTextField.OnEnterKey = _ => chatTextField.OnEnterKey = _ =>
{ {
if (chatTextField.Text.Length == 0) if (chatTextField.Text.Length == 0)
@@ -438,6 +441,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
chatTextField.OnEscKey = _ => chatTextField.YieldKeyboardFocus(); chatTextField.OnEscKey = _ => chatTextField.YieldKeyboardFocus();
chatDisabledLabel = new CachedTransform<int, string>(x => x > 0 ? $"Chat available in {x} seconds..." : "Chat Disabled");
lobbyChatPanel = lobby.Get<ScrollPanelWidget>("CHAT_DISPLAY"); lobbyChatPanel = lobby.Get<ScrollPanelWidget>("CHAT_DISPLAY");
chatTemplate = lobbyChatPanel.Get("CHAT_TEMPLATE"); chatTemplate = lobbyChatPanel.Get("CHAT_TEMPLATE");
lobbyChatPanel.RemoveChildren(); lobbyChatPanel.RemoveChildren();
@@ -487,6 +492,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
if (panel == PanelType.Options && OptionsTabDisabled()) if (panel == PanelType.Options && OptionsTabDisabled())
panel = PanelType.Players; panel = PanelType.Players;
var chatWasEnabled = chatEnabled;
chatEnabled = Game.RunTime >= TextNotificationsManager.ChatDisabledUntil && TextNotificationsManager.ChatDisabledUntil != uint.MaxValue;
if (chatEnabled && !chatWasEnabled)
{
chatTextField.Text = "";
if (Ui.KeyboardFocusWidget == null)
chatTextField.TakeKeyboardFocus();
}
else if (!chatEnabled)
{
var remaining = 0;
if (TextNotificationsManager.ChatDisabledUntil != uint.MaxValue)
remaining = (int)(TextNotificationsManager.ChatDisabledUntil - Game.RunTime + 999) / 1000;
chatTextField.Text = chatDisabledLabel.Update(remaining);
}
} }
void AddChatLine(TextNotification chatLine) void AddChatLine(TextNotification chatLine)

View File

@@ -19,10 +19,12 @@ set EnableGeoIP=True
set EnableLintChecks=True set EnableLintChecks=True
set ShareAnonymizedIPs=True set ShareAnonymizedIPs=True
set JoinChatDelay=5000
set SupportDir="" set SupportDir=""
:loop :loop
bin\OpenRA.Server.exe Engine.EngineDir=".." Game.Mod=%Mod% Server.Name=%Name% Server.ListenPort=%ListenPort% Server.AdvertiseOnline=%AdvertiseOnline% Server.EnableSingleplayer=%EnableSingleplayer% Server.Password=%Password% Server.RecordReplays=%RecordReplays% Server.RequireAuthentication=%RequireAuthentication% Server.ProfileIDBlacklist=%ProfileIDBlacklist% Server.ProfileIDWhitelist=%ProfileIDWhitelist% Server.EnableSyncReports=%EnableSyncReports% Server.EnableGeoIP=%EnableGeoIP% Server.EnableLintChecks=%EnableLintChecks% Server.ShareAnonymizedIPs=%ShareAnonymizedIPs% Engine.SupportDir=%SupportDir% bin\OpenRA.Server.exe Engine.EngineDir=".." Game.Mod=%Mod% Server.Name=%Name% Server.ListenPort=%ListenPort% Server.AdvertiseOnline=%AdvertiseOnline% Server.EnableSingleplayer=%EnableSingleplayer% Server.Password=%Password% Server.RecordReplays=%RecordReplays% Server.RequireAuthentication=%RequireAuthentication% Server.ProfileIDBlacklist=%ProfileIDBlacklist% Server.ProfileIDWhitelist=%ProfileIDWhitelist% Server.EnableSyncReports=%EnableSyncReports% Server.EnableGeoIP=%EnableGeoIP% Server.EnableLintChecks=%EnableLintChecks% Server.ShareAnonymizedIPs=%ShareAnonymizedIPs% Server.JoinChatDelay=%JoinChatDelay% Engine.SupportDir=%SupportDir%
goto loop goto loop

View File

@@ -29,6 +29,8 @@ EnableGeoIP="${EnableGeoIP:-"True"}"
EnableLintChecks="${EnableLintChecks:-"True"}" EnableLintChecks="${EnableLintChecks:-"True"}"
ShareAnonymizedIPs="${ShareAnonymizedIPs:-"True"}" ShareAnonymizedIPs="${ShareAnonymizedIPs:-"True"}"
JoinChatDelay="${JoinChatDelay:-"5000"}"
SupportDir="${SupportDir:-""}" SupportDir="${SupportDir:-""}"
while true; do while true; do
@@ -46,5 +48,6 @@ while true; do
Server.EnableGeoIP="$EnableGeoIP" \ Server.EnableGeoIP="$EnableGeoIP" \
Server.EnableLintChecks="$EnableLintChecks" \ Server.EnableLintChecks="$EnableLintChecks" \
Server.ShareAnonymizedIPs="$ShareAnonymizedIPs" \ Server.ShareAnonymizedIPs="$ShareAnonymizedIPs" \
Server.JoinChatDelay="$JoinChatDelay" \
Engine.SupportDir="$SupportDir" Engine.SupportDir="$SupportDir"
done done