diff --git a/Content.Server/_NF/Language/LanguageSystem.Networking.cs b/Content.Server/_NF/Language/LanguageSystem.Networking.cs index aae8e951e58..f91a411b6b1 100644 --- a/Content.Server/_NF/Language/LanguageSystem.Networking.cs +++ b/Content.Server/_NF/Language/LanguageSystem.Networking.cs @@ -14,6 +14,11 @@ public void InitializeNet() { // Refresh the client's state when its mind hops to a different entity SubscribeLocalEvent((uid, _, _) => SendLanguageStateToClient(uid)); + SubscribeLocalEvent((_, _, args) => + { + if (args.Mind.Comp.Session != null) + SendLanguageStateToClient(args.Mind.Comp.Session); + }); SubscribeLocalEvent((uid, comp, _) => SendLanguageStateToClient(uid, comp)); SubscribeNetworkEvent((_, session) => SendLanguageStateToClient(session.SenderSession)); diff --git a/Content.Server/_NF/Language/LanguageSystem.cs b/Content.Server/_NF/Language/LanguageSystem.cs index a41cfbe1f63..efea6b513a9 100644 --- a/Content.Server/_NF/Language/LanguageSystem.cs +++ b/Content.Server/_NF/Language/LanguageSystem.cs @@ -171,7 +171,7 @@ public LanguagePrototype GetLanguage(EntityUid speaker, LanguageSpeakerComponent // public void SetLanguage(EntityUid speaker, string language, LanguageSpeakerComponent? languageComp = null) { - if (!CanSpeak(speaker, language)) + if (!CanSpeak(speaker, language) || HasComp(speaker)) return; if (languageComp == null && !TryComp(speaker, out languageComp)) @@ -204,30 +204,32 @@ private static bool IsSentenceEnd(char ch) return ch is '.' or '!' or '?'; } - // This event is reused because re-allocating it each time is way too costly. - private readonly DetermineEntityLanguagesEvent _determineLanguagesEvent = new(string.Empty, new(), new()); - /// /// Returns a pair of (spoken, understood) languages of the given entity. /// public (List, List) GetAllLanguages(EntityUid speaker) { var languages = GetLanguages(speaker); - if (languages == null) - return (new(), new()); - // The lists need to be copied because the internal ones are re-used for performance reasons. return (new List(languages.SpokenLanguages), new List(languages.UnderstoodLanguages)); } + // This event is reused because re-allocating it each time is way too costly. + private readonly DetermineEntityLanguagesEvent _determineLanguagesEvent = new(string.Empty, new(), new()), + _universalLanguagesEvent = new(UniversalPrototype, [UniversalPrototype], [UniversalPrototype]); // Used for universal speakers only; never mutated + /// /// Dynamically resolves the current language of the entity and the list of all languages it speaks. /// The returned event is reused and thus must not be held as a reference anywhere but inside the caller function. + /// + /// If the entity is not a language speaker, or is a universal language speaker, then it's assumed to speak Universal, + /// aka all languages at once and none at the same time. /// - private DetermineEntityLanguagesEvent? GetLanguages(EntityUid speaker, LanguageSpeakerComponent? comp = null) + private DetermineEntityLanguagesEvent GetLanguages(EntityUid speaker, LanguageSpeakerComponent? comp = null) { - if (comp == null && !TryComp(speaker, out comp)) - return null; + // This is a shortcut for ghosts and entities that should not speak normally (admemes) + if (HasComp(speaker) || !TryComp(speaker, out comp)) + return _universalLanguagesEvent; var ev = _determineLanguagesEvent; ev.SpokenLanguages.Clear();