Open
Conversation
Copilot created this pull request from a session on behalf of
dgkanatsios
April 30, 2026 20:10
View session
4dfa53f to
2aea7a9
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an Unreal Engine “drop-in” runtime plugin sample that collects dedicated-server metrics and flushes them to PlayFab Telemetry (WriteTelemetryEvents), mirroring the previously added Unity MPS telemetry sample.
Changes:
- Added
UnrealMpsTelemetryruntime plugin scaffold withUMpsTelemetrySubsystemfor MPS lifecycle wiring. - Implemented metrics collection (tick/process stats) and a batching HTTP telemetry client with bounded queue + requeue-on-failure.
- Added Unreal-specific documentation and linked the sample from the repo root README.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| UnrealMpsTelemetry/UnrealMpsTelemetry.uplugin | Adds plugin descriptor for the Unreal telemetry sample. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/UnrealMpsTelemetry.Build.cs | Declares runtime module dependencies needed for HTTP + JSON telemetry. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Public/MpsTelemetrySubsystem.h | Introduces the public subsystem API for init/shutdown and custom metrics providers. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/MpsTelemetrySubsystem.cpp | Wires tick + ticker-based periodic summary collection and flush behavior. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Public/MpsTelemetrySampleConfig.h | Centralizes cadence, queue limits, and env-var telemetry key lookup. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/UnrealServerMetricsCollector.h | Defines the metrics collector interface and interval counters. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/UnrealServerMetricsCollector.cpp | Implements tick/process metric aggregation and custom provider invocation. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/PlayFabTelemetryClient.h | Defines the batching/queueing telemetry transport client. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/PlayFabTelemetryClient.cpp | Implements batching, HTTP POST to WriteTelemetryEvents, and retry/requeue. |
| UnrealMpsTelemetry/Source/UnrealMpsTelemetry/Private/UnrealMpsTelemetryModule.cpp | Adds module entry point and defines the plugin log category. |
| UnrealMpsTelemetry/README.md | Documents integration steps, telemetry key setup, and validation queries. |
| UnrealMpsTelemetry/.gitignore | Ignores Unreal plugin build artifacts. |
| README.md | Links the new Unreal telemetry sample from the root documentation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
dgkanatsios
pushed a commit
that referenced
this pull request
Apr 30, 2026
Fixes the four findings from the GitHub Copilot reviewer on PR #97: 1. PlayFabTelemetryClient.cpp - the HTTP completion lambda now captures a TSharedRef (not TWeakPtr) of the client. If the owning subsystem releases its TSharedPtr while a request is in flight (e.g. during Deinitialize), the dequeued batch was previously lost. The strong ref keeps the client alive until the request completes. 2. MpsTelemetrySubsystem.cpp - Deinitialize now calls BeginShutdown when the host did not, so server_metrics_final is always enqueued. Combined with completion chaining (see #1) and the strong-ref capture, in-flight and queued events keep flushing even after the subsystem releases its pointer. 3. PlayFabTelemetryClient.cpp - bFlushInProgress is now read and written under SyncRoot, including a single critical section that performs the in-progress check, dequeue, and flag flip together. Two concurrent FlushAsync calls can no longer both pull a batch and start parallel HTTP requests. The dedicated DequeueBatch helper is removed; ClearFlushInProgress is added so failure paths reliably reset the flag under the lock. 4. PlayFabTelemetryClient.cpp - the failure-path log truncates the response body to 512 chars to keep server logs bounded under sustained 5xx storms. Also: the HTTP completion lambda now performs completion chaining. After clearing bFlushInProgress, if pending events remain (because new ones were enqueued, requeued after a failure, or queued by BeginShutdown after the periodic ticker was removed) it kicks off another FlushAsync. This is what drains the queue during shutdown without relying on a sleep-based poll loop, which would block the same game thread that delivers HTTP completions. Not verified end-to-end on this commit (Unreal Engine has been uninstalled locally). Previous commits in this PR were verified against UE 5.7.4 + VS 2026; these changes are surgical and use only APIs already exercised in this plugin (FCriticalSection / FScopeLock, TSharedRef + AsShared, FString::Left). A teammate with Unreal installed should re-run the smoke test before merge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds an Unreal Engine drop-in runtime plugin that collects dedicated-server engine and process metrics and flushes them to PlayFab telemetry (WriteTelemetryEvents). Mirrors the design of the previously merged Unity MPS telemetry sample.
Plugin layout under UnrealMpsTelemetry/:
- UnrealMpsTelemetry.uplugin and Source/UnrealMpsTelemetry/UnrealMpsTelemetry.Build.cs declare a runtime module depending only on Core, CoreUObject, Engine, HTTP, Json.
- Public/MpsTelemetrySubsystem.h exposes UMpsTelemetrySubsystem (UGameInstanceSubsystem + FTickableGameObject) with InitializeForMps(TitleId, ExternalEntityId), BeginShutdown, and RegisterCustomMetricsProvider.
- Public/MpsTelemetrySampleConfig.h centralizes cadence, queue limits, event namespace (custom.mps.unrealserver), and resolves the telemetry key from PF_MPS_SECRET_TelemetryKey, then PLAYFAB_TELEMETRY_KEY, then a source placeholder.
- Private/UnrealServerMetricsCollector.{h,cpp} aggregates uptime, tick count, average and max tick ms, long-tick count, processor count, process and available physical and virtual memory, and invokes registered custom metrics providers.
- Private/PlayFabTelemetryClient.{h,cpp} batches and POSTs events to https://<title>.playfabapi.com/Event/WriteTelemetryEvents with the X-TelemetryKey header. The queue is bounded; failed batches requeue with oldest-event-first eviction matching the Unity sample.
Sends one server_metrics_summary every 60 seconds and one server_metrics_final on BeginShutdown to keep telemetry cost bounded.
Robustness:
- TPimplPtr<FUnrealServerMetricsCollector> hides the private collector header from the public subsystem header without breaking UHT-generated code.
- Periodic summary and flush use FTSTicker so cadence is real-time and unaffected by world pause or time dilation.
- HTTP completion lambda captures a strong TSharedRef of the client so in-flight batches survive subsystem teardown, and chains another FlushAsync if events remain so the queue drains during shutdown without sleep-based polling.
- Deinitialize calls BeginShutdown when the host did not, ensuring server_metrics_final is always enqueued.
- bFlushInProgress and the dequeue path are protected by a single FCriticalSection so concurrent FlushAsync calls cannot start parallel HTTP requests, and failure paths reliably reset the flag.
- Failure-path log truncates the HTTP response body to 512 chars to keep server logs bounded under sustained 5xx storms.
Verified end-to-end against UE 5.7.4 + Visual Studio 2026 (Microsoft.VisualStudio.Workload.NativeGame): the plugin compiles, the subsystem initializes, and server_metrics_summary events arrive in PlayFab Data Explorer at the expected 60s cadence under namespace custom.mps.unrealserver.
Includes README with integration steps, telemetry key setup, an actual sanitized ingested-envelope sample captured from a verification run, and a Data Explorer KQL query.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2fce145 to
c0c289d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR #96 added a Unity MPS telemetry sample. This adds the Unreal equivalent: a drop-in dedicated server telemetry plugin intended to be wired into an existing GSDK-enabled Unreal server.
Unreal telemetry plugin
UnrealMpsTelemetryruntime plugin scaffold.UMpsTelemetrySubsystemfor MPS lifecycle integration.PF_MPS_SECRET_TelemetryKeyorPLAYFAB_TELEMETRY_KEY.Metrics collection
server_metrics_summaryandserver_metrics_finalevents.PlayFab telemetry transport
WriteTelemetryEventswithX-TelemetryKey.Documentation
Example integration:
UMpsTelemetrySubsystem* Telemetry = GetGameInstance()->GetSubsystem<UMpsTelemetrySubsystem>(); Telemetry->RegisterCustomMetricsProvider( [this](TSharedRef<FJsonObject> Payload) { Payload->SetNumberField(TEXT("networkActiveConnections"), NetworkServer->GetActiveConnectionCount()); Payload->SetNumberField(TEXT("networkBytesReceived"), NetworkServer->GetAndResetBytesReceived()); Payload->SetNumberField(TEXT("networkBytesSent"), NetworkServer->GetAndResetBytesSent()); }); Telemetry->InitializeForMps(TitleId);