#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 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 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 MakeDefaultSink() const; TUniquePtr Sink; FGuid SessionId; bool bCheatsUsed = false; FString BuildString; };