Skip to content

Dedicated server SIGSEGV on every player join (dev): FlavorTextRandomizer parses config path as JSON content #285

Description

@Cadacious

Summary

On the current dev branch, a dedicated server crashes with SIGSEGV every time any player joins the session. Repro rate is 100% (observed on every join attempt). Root cause is in ANotifications::FlavorTextRandomizer, which parses a config value (a file path string) as JSON content — the deserialize fails and the subsequent FlavorJson->TryGetStringArrayField(...) dereferences an invalid TSharedPtr, tripping Assertion failed: IsValid() [SharedPointer.h:1082].

Environment

  • FRM: dev @ a0b6056d ("Worked on my machine...") — installed from the green CI run's Linux Server Build / Client Build artifacts
  • SML: 3.12.0 (release)
  • Game: Satisfactory 1.2.2.2 CL#491125, dedicated server
  • Server platform: Linux x64 (Ubuntu 22.04 container, wolveix/satisfactory-server), mod installed at FactoryGame/Mods/GameFeatures/FicsitRemoteMonitoring/
  • Client: Windows, same FRM commit + SML 3.12.0

Mod loads cleanly (GameFeature activates, all 92 HTTP endpoints register, /frm chat command registers). The crash is join-time only.

Expected vs actual

  • Expected: player joins; the PlayerLogin webhook/notification path either sends a webhook (if configured) or no-ops.
  • Actual: Join succeeded is immediately followed by Assertion failed: IsValid() → SIGSEGV; the server dies and restarts. With no DiscIT.URL configured at all, the notification path still executes and crashes.

Stack trace (symbolized, from the server log)

Assertion failed: IsValid() [File:Runtime/Core/Public\Templates/SharedPointer.h] [Line: 1082]
Unhandled Exception: SIGSEGV: invalid attempt to write memory at address 0x0000000000000003

libFactoryServer-FicsitRemoteMonitoring-Linux-Shipping.so!ANotifications::FlavorTextRandomizer(EFlavorType) [SharedPointer.h:1082]
libFactoryServer-FicsitRemoteMonitoring-Linux-Shipping.so!ANotifications::SendWebhook(TMap<FString, FString, ...>, EFlavorType) [Notifications.cpp:61]
libFactoryServer-FicsitRemoteMonitoring-Linux-Shipping.so!ANotifications::execSendWebhook(UObject*, FFrame&, void*) [Notifications.gen.cpp:197]
libFactoryServer-CoreUObject-Linux-Shipping.so!UFunction::Invoke(...)
... (LatentActionManager -> UWorld::Tick -> UGameEngine::Tick)

Root cause (3 stacked issues in Source/FicsitRemoteMonitoring/Private/Libraries/Notifications.cpp)

  1. FlavorTextRandomizer never loads the file. The config value (default: a file path) is fed directly to the JSON reader as if it were JSON content, and the result is used without an IsValid() guard:
FString WebhookJson = UFRMConfigManager::GetConfigOrDefault<FString>("DiscIT.FlavorJSON",
    FPaths::ProjectDir() + "Mods/GameFeatrures/FicsitRemoteMonitoring/JSON/Flavor.json");

TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(WebhookJson); // parses the PATH STRING
FJsonSerializer::Deserialize(Reader, FlavorJson);                             // fails -> FlavorJson invalid
FlavorJson->TryGetStringArrayField(TEXT("Any"), FlavorArray);                 // CRASH

Compare SendWebhook, which correctly calls URemoteMonitoringLibrary::FileLoadString(JsonPath, WebhookJson) first. FlavorTextRandomizer is missing that call.

  1. Typo in every default path: Mods/GameFeatrures/ ("GameFeatrures" — extra r) in all 11 GetConfigOrDefault defaults in this file, so the defaults can never resolve even via the file-loading path.

  2. Default filenames don't match shipped JSON: defaults reference Flavor.json and PlayerLogin.json, but the plugin ships FlavorText.json and PlayerOnline.json (JSON/ directory).

Also worth noting: SendWebhook runs unconditionally on the player-join event even when DiscIT.URL is empty — an early-out when no webhook URL is configured would avoid all of this work on servers that don't use webhooks.

Workaround (confirmed working)

Because the bug parses the config value as JSON content, supplying JSON content as the value avoids the crash. In Server Manager → Server Settings → Ficsit Remote MonitoringFlavor Text Array JSON Path, set:

{"Any":["[FRM]"],"Player":["{Player} changed online status"],"Positive":[":)"],"Detrimental":[":("],"Power":["power event"],"Battery":["battery level"],"Train":["train event"],"Doggo":["doggo found something"],"Research":["research done"]}

(All category keys populated, since the post-parse random pick from an empty array would be the next failure.) After Apply + server restart, players join normally — verified.

Suggested resolution

In FlavorTextRandomizer:

  • call URemoteMonitoringLibrary::FileLoadString(JsonPath, WebhookJson) (matching SendWebhook) before creating the JSON reader, and
  • guard the deserialize result (if (!FlavorJson.IsValid()) { log + return fallback; }) so a missing/corrupt file can never crash the server,
  • fix the GameFeatruresGameFeatures typo in all defaults,
  • align default filenames with the shipped JSON (FlavorText.json, PlayerOnline.json).

Happy to open a PR with the above if useful (compile-untested locally — no UE toolchain here — but mechanical, and your CI builds PRs).

Minor related observation

In the Server Manager settings UI on the same build, the last three FRM fields appear label-shifted ("Train Error JSON Path" / "Train Idle JSON Path" both show the value 180, which presumably belongs to "Train Idle Delay") — possibly an ordering issue in the new config registration.


Diagnosis assisted by Claude Code; logs and repro from a live dedicated server.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions