Files
UnrealPrototyping/Source/Salty/Core/TelemetrySubsystem.h
T
Rainer LeitandClaude Fable 5.1 3e52d26fb7 Step 2: the telemetry seam
ITelemetrySink with the null, log and JSON Lines sinks, FTelemetryEvent and the envelope, TelemetryEvents,
UTelemetrySubsystem on the game instance, ASaltyGameMode minting the session id into the replicated
ASaltyGameState, bs.TelemetryTest, the git hash in the build string (D-42) and four Salty.Core.Telemetry tests
replacing the placeholder. Proved standalone and with a headless server plus client sharing one session id.

Also enables the engine's Editor, AutomationTest, GameplayTags, ConfigSettings and LiveCoding MCP toolsets
(D-43) so the editor's MCP server exposes more than the skills toolset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 20:22:20 +03:00

46 lines
1.7 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Telemetry/TelemetryEvent.h"
#include "Telemetry/TelemetrySink.h"
#include "TelemetrySubsystem.generated.h"
// The one seam gameplay emits through. Owns the sink, stamps the envelope, exposes Emit. Gameplay code resolves it
// from its game instance once and keeps the pointer; there is no static helper and there will not be one
// (Docs/Spec/Telemetry.md). Every peer has its own subsystem and its own file; nothing is forwarded.
UCLASS()
class SALTY_API UTelemetrySubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// Stamps the envelope and hands the event to the sink. Name comes from TelemetryEvents, never a literal.
void Emit(FName Name, TSharedPtr<FJsonObject> Payload = nullptr);
// The server mints the id in ASaltyGameMode; a client adopts the id replicated through ASaltyGameState.
void BeginSession(FGuid InSessionId);
// Log in the editor, Jsonl with -telemetry or telemetry.File 1, Null otherwise. Public so a test can substitute.
void SetSink(TUniquePtr<ITelemetrySink> InSink);
// Called by every bs.* console command. Sticky for the session: tainted sessions are filterable, not deleted.
void MarkCheatUsed(FName Command = NAME_None);
bool HasSession() const { return SessionId.IsValid(); }
FGuid GetSessionId() const { return SessionId; }
bool WereCheatsUsed() const { return bCheatsUsed; }
private:
FTelemetryEnvelope StampEnvelope() const;
TUniquePtr<ITelemetrySink> MakeDefaultSink() const;
TUniquePtr<ITelemetrySink> Sink;
FGuid SessionId;
bool bCheatsUsed = false;
FString BuildString;
};