diff --git a/CelesteNet.Client/CelesteNetClient.cs b/CelesteNet.Client/CelesteNetClient.cs index eaedd385..870fd45d 100644 --- a/CelesteNet.Client/CelesteNetClient.cs +++ b/CelesteNet.Client/CelesteNetClient.cs @@ -318,8 +318,12 @@ public void Handle(CelesteNetConnection con, DataPlayerInfo info) { } public bool Filter(CelesteNetConnection con, DataPlayerInfo info) { - if (info != null && Options.AvatarsDisabled) + if (info == null) + return false; + + if(Options.AvatarsDisabled) { info.DisplayName = info.FullName; + } return true; } diff --git a/CelesteNet.Client/Components/CelesteNetEmojiComponent.cs b/CelesteNet.Client/Components/CelesteNetEmojiComponent.cs index 699d7636..d8532814 100644 --- a/CelesteNet.Client/Components/CelesteNetEmojiComponent.cs +++ b/CelesteNet.Client/Components/CelesteNetEmojiComponent.cs @@ -1,15 +1,16 @@ -using Celeste.Mod.CelesteNet.DataTypes; +using System; +using System.Collections.Generic; +using System.IO; +using Celeste.Mod.CelesteNet.DataTypes; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Monocle; -using System; -using System.Collections.Generic; -using System.IO; -namespace Celeste.Mod.CelesteNet.Client.Components -{ +namespace Celeste.Mod.CelesteNet.Client.Components { public class CelesteNetEmojiComponent : CelesteNetGameComponent { + public const string AvatarMissing = ":celestenet_avatar_missing:"; + public class NetEmojiContent : ModContent { public HashSet Registered = new(); @@ -123,6 +124,14 @@ public void Handle(CelesteNetConnection con, DataNetEmoji netemoji) { Content.Registered.Add(asset.ID); Emoji.Register(asset.ID, tex); Emoji.Fill(CelesteNetClientFont.Font); + + // find the corresponding DataPlayerInfo (perhaps DataNetEmoji should hold a Ref) + string[] splitID = netemoji.ID.Split('_'); + if (splitID.Length > 2 && uint.TryParse(splitID[2], out uint ID) + && Client?.Data != null && Client.Data.TryGetRef(ID, out DataPlayerInfo player) && player != null) { + // restore the avatar in case it has been set to AvatarMissing by the Filter below + player.DisplayName = $":{netemoji.ID}: {player.FullName}"; + } }); } catch (ObjectDisposedException) { // Main thread died and queue closed, whoops. @@ -131,6 +140,18 @@ public void Handle(CelesteNetConnection con, DataNetEmoji netemoji) { } } + public bool Filter(CelesteNetConnection con, DataPlayerInfo info) { + if (info == null || Client?.Options == null || Client.Options.AvatarsDisabled) + return true; + + // catch missing avatars - "restoring" these happens in Handle above when the avatar is fully received + string avatar = $"celestenet_avatar_{info.ID}_"; + if (!Emoji.Registered.Contains(avatar)) { + info.DisplayName = info.DisplayName.Replace($":{avatar}:", AvatarMissing); + } + return true; + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/CelesteNet.Server/CelesteNetServer.cs b/CelesteNet.Server/CelesteNetServer.cs index 677d37f2..84f013f9 100644 --- a/CelesteNet.Server/CelesteNetServer.cs +++ b/CelesteNet.Server/CelesteNetServer.cs @@ -1,5 +1,4 @@ -using Celeste.Mod.CelesteNet.DataTypes; -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -10,6 +9,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Celeste.Mod.CelesteNet.DataTypes; namespace Celeste.Mod.CelesteNet.Server { public class CelesteNetServer : IDisposable { @@ -133,7 +133,7 @@ public CelesteNetServer(CelesteNetServerSettings settings) { PingRequestTimer = new(Settings.PingRequestInterval); PingRequestTimer.Elapsed += (_, _) => SendPingRequests(); - AvatarSendTimer = new(Settings.HeartbeatInterval); + AvatarSendTimer = new(Settings.AvatarQueueInterval); AvatarSendTimer.Elapsed += (_, _) => ClearAvatarQueues(); } @@ -436,13 +436,13 @@ private void ClearAvatarQueues() { if (ses.AvatarSendQueue.Count > 0) { - foreach (CelesteNetPlayerSession other in ses.AvatarSendQueue.Take(10).ToList()) { + foreach (CelesteNetPlayerSession other in ses.AvatarSendQueue.Take(Settings.AvatarQueueBatchCount).ToList()) { foreach (DataInternalBlob frag in other.AvatarFragments) { ses.Con.Send(frag); } ses.AvatarSendQueue.Remove(other); } - Logger.Log(LogLevel.DBG, "main", $"ClearAvatarQueues: Sent 10 players' avatar frags to {ses.Name}"); + Logger.Log(LogLevel.DBG, "main", $"ClearAvatarQueues: Sent ~{Settings.AvatarQueueBatchCount} players' avatar frags to {ses.Name}"); } } diff --git a/CelesteNet.Server/CelesteNetServerSettings.cs b/CelesteNet.Server/CelesteNetServerSettings.cs index 73ae0021..723d0c6f 100644 --- a/CelesteNet.Server/CelesteNetServerSettings.cs +++ b/CelesteNet.Server/CelesteNetServerSettings.cs @@ -1,8 +1,7 @@ using Newtonsoft.Json; using YamlDotNet.Serialization; -namespace Celeste.Mod.CelesteNet.Server -{ +namespace Celeste.Mod.CelesteNet.Server { public class CelesteNetServerSettings : CelesteNetServerModuleSettings { [YamlIgnore] @@ -46,6 +45,8 @@ public override void Save(string path = "") { public int MaxHeartbeatDelay { get; set; } = 20; public float HeartbeatInterval { get; set; } = 250f; public float PingRequestInterval { get; set; } = 1500f; + public float AvatarQueueInterval { get; set; } = 250f; + public int AvatarQueueBatchCount { get; set; } = 10; public string PacketDumperDirectory { get; set; } = "packetDump"; public int PacketDumperMaxDumps { get; set; } = 64; diff --git a/Graphics/Atlases/Gui/emoji/celestenet_avatar_missing.png b/Graphics/Atlases/Gui/emoji/celestenet_avatar_missing.png new file mode 100644 index 00000000..cbc51aac Binary files /dev/null and b/Graphics/Atlases/Gui/emoji/celestenet_avatar_missing.png differ