From b9607e39b0d26d100bbac8d8a15e6aad8ab6cd60 Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Sun, 16 Aug 2026 09:07:42 +0000 Subject: [PATCH] Exception now meows when an error occurs on the server (#39734) * Exception now meows when an error occurs * tweaks * Add to real mouse * 1984 :pensive: * Sussy entity effect * Comment * More errors * Other fixes * opps * fixes * cleanup * Cleanup * Remove saving last message * runtime as well! * Make logger private, clean up error has occured to boolean instead of string * Add auto pause and time offset serializer * Properly block accent when speaking (sometimes) * Add package * Fix * Review * fix --- Content.Server/Content.Server.csproj | 1 + .../Effects/MakeSentientEntityEffectSystem.cs | 2 + .../RuntimeFun/SpeakOnExceptionComponent.cs | 41 ++++++++ .../RuntimeFun/SpeakOnExceptionSystem.cs | 96 +++++++++++++++++++ .../speak-on-exception/speak-on-exception.ftl | 14 +++ .../Entities/Mobs/NPCs/animal_speech.yml | 5 + .../Prototypes/Entities/Mobs/NPCs/pets.yml | 7 ++ 7 files changed, 166 insertions(+) create mode 100644 Content.Server/RuntimeFun/SpeakOnExceptionComponent.cs create mode 100644 Content.Server/RuntimeFun/SpeakOnExceptionSystem.cs create mode 100644 Resources/Locale/en-US/speak-on-exception/speak-on-exception.ftl create mode 100644 Resources/Prototypes/Entities/Mobs/NPCs/animal_speech.yml diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 298ad4ea0a..66d0b9ef2f 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -14,6 +14,7 @@ + diff --git a/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs index b1e2703995..c19f62742c 100644 --- a/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Ghost.Roles.Components; +using Content.Server.RuntimeFun; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects; using Content.Shared.Mind.Components; @@ -23,6 +24,7 @@ public sealed partial class MakeSentientEntityEffectSystem : EntityEffectSystem< RemComp(entity); // TODO: Make MonkeyAccent a replacement accent and remove MonkeyAccent code-smell. RemComp(entity); + RemComp(entity); } // Stops from adding a ghost role to things like people who already have a mind diff --git a/Content.Server/RuntimeFun/SpeakOnExceptionComponent.cs b/Content.Server/RuntimeFun/SpeakOnExceptionComponent.cs new file mode 100644 index 0000000000..16fb955040 --- /dev/null +++ b/Content.Server/RuntimeFun/SpeakOnExceptionComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Dataset; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.RuntimeFun; + +/// +/// Entities with this component will speak from a dataset everytime an error occurs in an interval. +/// +[RegisterComponent, AutoGenerateComponentPause] +public sealed partial class SpeakOnExceptionComponent : Component +{ + /// + /// Minimum time between error speech events. + /// + [DataField] + public TimeSpan SpeechCooldown = TimeSpan.FromMinutes(1); + + /// + /// The chance to speak without an accent. + /// + [DataField] + public float ChanceSpeakNoAccent = 0.005f; + + /// + /// Localized dataset used when speaking. + /// + [DataField] + public ProtoId Dataset = "ExceptionSpeechDataset"; + + /// + /// The next time the entity can say another error. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] + public TimeSpan? NextTimeCanSpeak; + + /// + /// If this component is currently trying to block accents from working. + /// + public bool BlockAccent; +} diff --git a/Content.Server/RuntimeFun/SpeakOnExceptionSystem.cs b/Content.Server/RuntimeFun/SpeakOnExceptionSystem.cs new file mode 100644 index 0000000000..da425d9535 --- /dev/null +++ b/Content.Server/RuntimeFun/SpeakOnExceptionSystem.cs @@ -0,0 +1,96 @@ +using Content.Server.Chat.Systems; +using Content.Shared.Chat; +using Content.Shared.Speech.EntitySystems; +using Robust.Shared.Random; +using Robust.Shared.Timing; +using Serilog.Events; + +namespace Content.Server.RuntimeFun; + +/// +/// System for the . Deals with getting the latest error log and making +/// entities with that component speak. +/// +public sealed partial class SpeakOnExceptionSystem : EntitySystem +{ + [Dependency] private IGameTiming _timing = default!; + [Dependency] private ILogManager _log = default!; + [Dependency] private IRobustRandom _random = default!; + [Dependency] private ChatSystem _chat = default!; + + // Special log handler that just saves the latest error. + private SpeakOnExceptionLogHandler _logHandler = default!; + + public override void Initialize() + { + base.Initialize(); + + _logHandler = new SpeakOnExceptionLogHandler(); + _log.RootSawmill.AddHandler(_logHandler); + + SubscribeLocalEvent(OnTransformSpeech, before: [ typeof(AccentSystem) ]); + } + + public override void Shutdown() + { + _log.RootSawmill.RemoveHandler(_logHandler); + } + + public override void Update(float frameTime) + { + if (!_logHandler.ErrorHasOccured) + return; + + _logHandler.ErrorHasOccured = false; + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (_timing.CurTime < comp.NextTimeCanSpeak) + continue; + + if (_random.Prob(comp.ChanceSpeakNoAccent)) + comp.BlockAccent = true; + + _chat.TrySendInGameICMessage(uid, CensorMessage(comp), InGameICChatType.Speak, ChatTransmitRange.Normal, true); + + comp.BlockAccent = false; + + comp.NextTimeCanSpeak += comp.SpeechCooldown; + } + } + + [SubscribeLocalEvent] + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextTimeCanSpeak = _timing.CurTime; + } + + private void OnTransformSpeech(Entity ent, ref TransformSpeechEvent args) + { + if (ent.Comp.BlockAccent) + args.Cancel(); + } + + private string CensorMessage(SpeakOnExceptionComponent comp) + { + return Loc.GetString(_random.Pick(ProtoMan.Index(comp.Dataset).Values)); + } + + // Log handler for SpeakOnException entities. + private sealed class SpeakOnExceptionLogHandler : ILogHandler + { + // Gets set to true if an error ever occurs - reset this to false if you want to see if another error has occurred! + public bool ErrorHasOccured; + + public void Log(string sawmillName, LogEvent message) + { + if (message.Exception == null) + return; + + ErrorHasOccured = true; + } + } +} + diff --git a/Resources/Locale/en-US/speak-on-exception/speak-on-exception.ftl b/Resources/Locale/en-US/speak-on-exception/speak-on-exception.ftl new file mode 100644 index 0000000000..d67d960bfe --- /dev/null +++ b/Resources/Locale/en-US/speak-on-exception/speak-on-exception.ftl @@ -0,0 +1,14 @@ +exception-censored-1 = called `Option::unwrap()` on a `None` value` +exception-censored-2 = SyntaxError: invalid syntax +exception-censored-3 = java.lang.NullPointerException: Cannot invoke "com.spacestation14.ss14.Cat.meow(java.lang.String)" because the return value of "com.spacestation14.robusttoolbox.Utils.getCatSound()" is null +exception-censored-4 = Error: Can't open display: x3 +exception-censored-5 = Segmentation fault (core dumped) +exception-censored-6 = cat-speech-dataset-120 +exception-censored-7 = Can't resolve "Robust.Shared.GameObjects.MetaDataComponent" on entity 5233063! +exception-censored-8 = Uncaught ReferenceError: meow is not defined +exception-censored-9 = TabError: inconsistent use of tabs and spaces in indentation +exception-censored-10 = code.dm:3:error: meow: undefined proc +exception-censored-11 = Failed to wipe mind: Exception +exception-censored-12 = Guru Meditation: #0000000F.0000ACA7 +exception-censored-13 = Unexpected token 'w' +exception-censored-14 = Operation timed out: AwaitNap() diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animal_speech.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animal_speech.yml new file mode 100644 index 0000000000..3eab487c85 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animal_speech.yml @@ -0,0 +1,5 @@ +- type: localizedDataset + id: ExceptionSpeechDataset + values: + prefix: exception-censored- + count: 14 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 9b0cb337f8..74aa2bf7ec 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -156,6 +156,7 @@ attributes: proper: true gender: female + - type: SpeakOnException - type: entity parent: [ MobCorgiPuppy, BaseSlicingRefinable ] @@ -197,6 +198,9 @@ - VimPilot - type: StealTarget stealGroup: AnimalNamedCat + - type: SpeakOnException + speechCooldown: 3m + chanceSpeakNoAccent: 0.01 - type: entity parent: [ BaseUniquePet, MobCatCalico ] @@ -207,6 +211,9 @@ - type: NpcFactionMember factions: - PetsNT + - type: SpeakOnException + speechCooldown: 2m + chanceSpeakNoAccent: 0.007 - type: Grammar attributes: proper: true