feat: Implement BPC_PlatformServiceAbstraction for unified platform detection and SDK routing

- Added BPC_PlatformServiceAbstraction to centralize platform detection and SDK routing for achievements, cloud saves, and overlays.
- Updated dependencies across various systems to utilize the new platform service for consistent platform handling.
- Deprecated old platform enums in favor of a unified EPlatformFamily enum.
- Enhanced documentation for affected systems to reflect changes in platform handling and dependencies.
This commit is contained in:
Lefteris Notas
2026-05-22 18:22:42 +03:00
parent dc9c1a6b98
commit 15d6e88780
12 changed files with 439 additions and 25 deletions

View File

@@ -0,0 +1,406 @@
# 150 — Platform Service Abstraction (`BPC_PlatformServiceAbstraction`)
> **Blueprint-Only Implementation** — UE 5.55.7 supports all platform detection and SDK routing from Blueprints via `UGameplayStatics::GetPlatformName()`, conditional compilation checks, and plugin API calls. This component is the **single source of truth** for platform identity, replacing the three fragmented detection systems that currently exist across the framework.
---
## Purpose
Central platform authority attached to the Game Instance. Detects the platform once at startup and provides a unified `EPlatformFamily` enum that ALL subsystems query instead of detecting platform independently. Routes platform-specific SDK calls (achievement submission to Steam/PSN/Xbox Live/Nintendo, cloud saves, overlays, input device profiles). Returns stub/no-op implementations on unsupported platforms so game code never needs `#if PLATFORM_PS5` checks.
## The Problem Being Solved
Currently the framework has **three independent platform detection systems**, each calling `GetPlatformName()` separately:
| System | Enum | Platform Families |
|--------|------|-------------------|
| `BPC_RenderPipelineManager` (149) | `EPlatformFamily` | PS5, PS4, Xbox_Series, Xbox_One, Switch, PC_High, PC_Low, SteamDeck |
| `SS_EnhancedInputManager` (128) | `E_InputPlatform` | PC_KeyboardMouse, Xbox, PS5_DualSense |
| `BPC_HapticsController` (148) | `EControllerPlatform` | PC_Generic, Xbox, PS5_DualSense, PS4_DualShock |
These are **different enums with different values** and don't talk to each other. If you change platform in one system, nothing cascades.
**After this component is integrated, all three systems query `BPC_PlatformServiceAbstraction.GetPlatformFamily()` instead of detecting platform themselves. The old enums are deprecated and mapped to `EPlatformFamily`.**
## Dependencies
- **Requires:** [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) (attached as component), [`DA_GameTagRegistry`](../01-core/01_DA_GameTagRegistry.md) (platform tag validation)
- **Required By:** [`BPC_RenderPipelineManager`](../12-settings/149_BPC_RenderPipelineManager.md) (GFX quality profile selection), [`SS_EnhancedInputManager`](../15-input/128_SS_EnhancedInputManager.md) (input device profile selection), [`BPC_HapticsController`](../12-settings/148_BPC_HapticsController.md) (controller platform detection), [`SS_AchievementSystem`](../11-meta/103_SS_AchievementSystem.md) (SDK routing), [`SS_SettingsSystem`](105_SS_SettingsSystem.md) (platform-aware defaults)
- **Engine/Plugin Requirements:** GameplayTags, `UGameplayStatics`, Platform SDK plugins (Steamworks, PSN, Xbox GDK, Nintendo SDK — all optional)
## Class Info
| Property | Value |
|----------|-------|
| **Parent Class** | `ActorComponent` |
| **Class Type** | Blueprint Component |
| **Asset Path** | `Content/Framework/Core/BPC_PlatformServiceAbstraction` |
| **Implements Interfaces** | None |
| **Attachment** | Game Instance (`GI_GameFramework`) |
---
## 1. Enums
### `EPlatformFamily` (UNIFIED — all systems use this)
| Value | Description | Input Device | Render Pipeline | Achievements SDK | Store |
|-------|-------------|-------------|-----------------|-----------------|-------|
| `Unknown = 0` | Not yet detected | — | — | — | — |
| `PC_Win64 = 1` | Windows PC (any GPU) | KB+M / Xbox / PS5 gamepad | Detected per GPU capability | Steam / EGS | Steam / EGS |
| `PC_Linux = 2` | Linux / SteamOS | Same as PC | Vulkan | Steam | Steam |
| `PS5 = 3` | PlayStation 5 | DualSense | Lumen + Nanite | PSN Trophies | PS Store |
| `PS5_Pro = 4` | PlayStation 5 Pro | DualSense | Lumen + PSSR | PSN Trophies | PS Store |
| `PS4 = 5` | PlayStation 4 / 4 Pro | DualShock 4 | Baked only | PSN Trophies | PS Store |
| `Xbox_Series = 6` | Xbox Series X\|S | Xbox Wireless | Lumen + Nanite | Xbox Live | MS Store |
| `Xbox_One = 7` | Xbox One / One X | Xbox Wireless | Baked only | Xbox Live | MS Store |
| `Switch = 8` | Nintendo Switch | Joy-Con / Pro | Baked + Proxy | Nintendo | eShop |
| `Switch_2 = 9` | Nintendo Switch 2 | Joy-Con 2 / Pro 2 | Baked + CSM + FSR | Nintendo | eShop |
| `SteamDeck = 10` | Steam Deck | Built-in / external | Baked/SSGI + CSM | Steam | Steam |
| `Mac = 11` | macOS | KB+M / gamepad | Metal / Baked | Steam | Steam / App Store |
### `EPlatformSDK`
| Value | Description |
|-------|-------------|
| `None = 0` | No SDK available (editor, generic build) |
| `Steam = 1` | Steamworks SDK (achievements, cloud, overlay, stats) |
| `PSN = 2` | PlayStation Network SDK (trophies, cloud, overlay, invites) |
| `XboxLive = 3` | Xbox Live / GDK (achievements, cloud, overlay, invites) |
| `Nintendo = 4` | Nintendo SDK (achievements, cloud, overlay) |
| `EGS = 5` | Epic Games Store SDK |
### `EPlatformOverlayType`
| Value | Description |
|-------|-------------|
| `Achievements = 0` | Show platform achievements/trophies page |
| `Friends = 1` | Open friends list |
| `Invite = 2` | Send game invite |
| `Store = 3` | Open store page (DLC, etc.) |
| `SocialFeed = 4` | Community / social feed |
---
## 2. Structs
### `SPlatformCapabilities`
| Field | Type | Description |
|-------|------|-------------|
| `Platform` | `EPlatformFamily` | Current platform |
| `SDK` | `EPlatformSDK` | Available platform SDK |
| `bSupportsLumen` | `bool` | Platform GPU supports Lumen GI |
| `bSupportsNanite` | `bool` | Platform GPU supports Nanite |
| `bSupportsHWRT` | `bool` | Platform supports hardware ray tracing |
| `bSupportsCloudSaves` | `bool` | Platform has cloud save API |
| `bSupportsAchievements` | `bool` | Platform has achievement/trophy system |
| `bSupportsOverlay` | `bool` | Platform has in-game overlay |
| `bSupportsRichPresence` | `bool` | Platform supports rich presence / "now playing" |
| `bSupportsUserGeneratedContent` | `bool` | Platform supports UGC/mod sharing |
| `bIsConsole` | `bool` | Is this a console platform (certification required)? |
| `bIsHandheld` | `bool` | Is this a handheld device? |
| `DefaultInputType` | `FName` | Default input device for this platform ("Gamepad", "Keyboard", "Touch") |
| `MaxLocalPlayers` | `int32` | Maximum local split-screen players |
| `StoreID` | `FString` | Platform store ID for DLC checks |
---
## 3. Variables
### Configuration (Instance Editable)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `bAutoDetectPlatform` | `bool` | `true` | `Config` | Auto-detect on Initialize |
| `OverridePlatform` | `EPlatformFamily` | `Unknown` | `Config` | Force a platform (for testing in editor) |
| `bEnableSDKIntegration` | `bool` | `true` | `Config` | Enable platform SDK calls |
### Internal (Private)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentPlatform` | `EPlatformFamily` | `Unknown` | `State` | Detected or overridden platform |
| `CurrentSDK` | `EPlatformSDK` | `None` | `State` | Active platform SDK |
| `PlatformCapabilities` | `SPlatformCapabilities` | — | `State` | Full capabilities snapshot |
| `bIsInitialized` | `bool` | `false` | `State` | Whether Initialize completed |
| `CachedGameInstance` | `GI_GameFramework` | `None` | `Cache` | Owner Game Instance reference |
---
## 4. Functions
### Public Functions
#### `Initialize` → `void`
- **Description:** Detect platform, resolve SDK, build capabilities, broadcast ready signal. Called once at Game Instance startup.
- **Flow:**
1. Get Owner → Cast to `GI_GameFramework` → cache
2. If `OverridePlatform != Unknown`: use override
3. Else if `bAutoDetectPlatform`: call `DetectPlatform()`
4. Resolve which SDK is available: check plugin state, build configuration
5. Build `SPlatformCapabilities` for detected platform
6. Register platform in `DA_GameTagRegistry` as `Framework.Platform.{Name}`
7. Set `bIsInitialized = true`
8. Broadcast `OnPlatformReady(CurrentPlatform)`
9. Broadcast `OnPlatformCapabilitiesReady(PlatformCapabilities)`
#### `DetectPlatform` → `EPlatformFamily`
- **Description:** Detects the current platform using UE5 APIs.
- **Flow:**
1. `UGameplayStatics::GetPlatformName()` → switch:
- "PS5" → check if Pro model available → `PS5_Pro` or `PS5`
- "PS4" → `PS4`
- "XboxOne" → `Xbox_One`
- "XSX" or "XboxAnaconda" → `Xbox_Series` (check if S model → adjust capabilities)
- "Switch" → check model revision → `Switch_2` or `Switch`
- "Win64" → check if running on Steam Deck (`IsSteamDeck()`) → `SteamDeck`
- "Win64" → `PC_Win64`
- "Linux" → `PC_Linux`
- "Mac" → `Mac`
2. Store in `CurrentPlatform`
3. Return `CurrentPlatform`
#### `GetPlatformFamily` → `EPlatformFamily`
- **Description:** Returns the current platform. **This is the function ALL other systems call.** Read-only, always available after initialization.
#### `GetSDK` → `EPlatformSDK`
- **Description:** Returns the available platform SDK. Used by achievement and save systems for routing.
#### `GetPlatformCapabilities` → `SPlatformCapabilities`
- **Description:** Returns the full capabilities struct. Read-only.
#### `SubmitAchievementToPlatform` → `bool`
- **Description:** Route an achievement unlock to the correct platform SDK.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `AchievementID` | `FString` | Platform-specific achievement ID |
- **Flow:**
1. Switch on `CurrentSDK`:
- `Steam` → call Steamworks `ISteamUserStats::SetAchievement(AchievementID)` + `StoreStats()`
- `PSN` → call PSN Trophy API `UnlockTrophy(AchievementID)`
- `XboxLive` → call Xbox Live `AchievementsService::UpdateAchievement(AchievementID, 100)`
- `Nintendo` → call Nintendo achievement API
- `None` → log "Achievement submitted (editor mode)" → return true (no-op stub)
2. Return true if successful, false on SDK error
#### `SyncCloudSave` → `bool`
- **Description:** Upload or sync save data to platform cloud.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `SlotIndex` | `int32` | Which save slot |
| `SaveData` | `TArray<uint8>` | Serialized save data |
- **Flow:** Route to Steam Cloud / PSN Cloud / Xbox Cloud / Nintendo Cloud based on `CurrentSDK`.
#### `ShowPlatformOverlay` → `void`
- **Description:** Open a platform-specific overlay.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `OverlayType` | `EPlatformOverlayType` | Which overlay to show |
- **Flow:**
1. Switch on `CurrentSDK`:
- `Steam``ISteamFriends::ActivateGameOverlay(URL)`
- `PSN` → System software overlay API
- Others → nop (not supported)
#### `GetDefaultInputProfile` → `FName`
- **Description:** Returns the default input profile for this platform. Used by `SS_EnhancedInputManager` to select the correct `DA_InputMappingProfile`.
- **Returns:** "PC_KeyboardMouse", "Xbox", "PS5_DualSense", "PS4_DualShock", "Switch_JoyCon"
#### `IsConsole` → `bool`, `IsHandheld` → `bool`, `IsPS5` → `bool`, `IsXbox` → `bool`, `IsSwitch` → `bool`, `IsPC` → `bool`, `IsSteamDeck` → `bool`
- **Description:** Convenience boolean checks for common platform queries. All return from `PlatformCapabilities` struct.
#### `GetConsoleCertificationRequirements` → `TArray<FText>`
- **Description:** Returns a list of TRC (Technical Requirement Checklist) items for the current console platform. Empty on PC.
- **Flow:**
1. Switch on `CurrentPlatform`:
- `PS5` → return Sony TRC items (60 FPS perf mode, HDR support, controller speaker, activity cards, etc.)
- `PS4` → return Sony PS4 TRC items
- `Xbox_Series` → return Microsoft XR items (Quick Resume, Smart Delivery, VRR, etc.)
- `Xbox_One` → return Microsoft XR items
- `Switch` → return Nintendo guidelines (dynamic resolution, Handheld/Docked parity, etc.)
- `PC_*` → return empty (no console cert needed)
#### `GetStoreID` → `FString`
- **Description:** Returns the platform store app ID for DLC entitlement checks.
---
## 5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnPlatformReady` | `EPlatformFamily Platform` | `Public` | Fired after platform detection completes — all other systems bind to this for deferred init |
| `OnPlatformCapabilitiesReady` | `SPlatformCapabilities Capabilities` | `Public` | Fired alongside OnPlatformReady with full capability info |
| `OnPlatformSDKReady` | `EPlatformSDK SDK` | `Public` | Fired when platform SDK is ready for API calls |
| `OnPlatformOverlayOpened` | `EPlatformOverlayType Type` | `Public` | Fired when platform overlay opens (game should pause) |
| `OnPlatformOverlayClosed` | — | `Public` | Fired when platform overlay closes (game resumes) |
| `OnCloudSaveCompleted` | `bool bSuccess`, `int32 SlotIndex` | `Public` | Fired when cloud save upload completes |
| `OnAchievementSubmitted` | `FString AchievementID`, `bool bSuccess` | `Public` | Fired after platform API achievement call |
---
## 6. Overridden Events
### Event: On Component Created (attached to GI_GameFramework via BeginPlay)
- **Description:** Auto-initializes at game start.
- **Flow:**
1. Call `Initialize()`
2. All dependent subsystems that bound to `OnPlatformReady` receive the event and complete their initialization
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[GI_GameFramework: BeginPlay] --> B[BPC_PlatformServiceAbstraction: Initialize]
B --> C{OverridePlatform?}
C -->|Yes| D[Use Override]
C -->|No| E[DetectPlatform]
E --> F[Switch on GetPlatformName]
F --> G[Set CurrentPlatform]
D --> G
G --> H[Resolve available SDK]
H --> I[Build SPlatformCapabilities]
I --> J[Register Platform Tag in DA_GameTagRegistry]
J --> K[Broadcast OnPlatformReady]
K --> L[ALL BOUND SYSTEMS RECEIVE]
L --> M[RenderPipelineManager: load DA_RPP for platform]
L --> N[EnhancedInputManager: load DA_InputMapping for platform]
L --> O[HapticsController: set EControllerPlatform from platform]
L --> P[AchievementSystem: route SDK via GetSDK]
L --> Q[SettingsSystem: load platform defaults]
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `GI_GameFramework` | `Owner` | Hosts this component |
| `BPC_PlatformServiceAbstraction` | `Dispatcher` → ALL systems | `OnPlatformReady(Platform)` — every system's deferred init trigger |
| `BPC_RenderPipelineManager` (149) | `Function Call` | `GetPlatformFamily()` → selects correct `DA_RenderPipelineProfile` |
| `SS_EnhancedInputManager` (128) | `Function Call` | `GetDefaultInputProfile()` → selects correct `DA_InputMappingProfile` |
| `BPC_HapticsController` (148) | `Function Call` | `GetPlatformFamily()` → maps to `EControllerPlatform` |
| `SS_AchievementSystem` (103) | `Function Call` | `SubmitAchievementToPlatform(ID)` → routes to Steam/PSN/Xbox/Nintendo |
| `SS_SaveManager` (35) | `Function Call` | `SyncCloudSave(Slot, Data)` → routes to platform cloud API |
| `SS_SettingsSystem` (105) | `Function Call` | `GetPlatformFamily()` → sets platform-appropriate defaults |
| `SS_UIManager` (44) | `Dispatcher` | `OnPlatformOverlayOpened/Closed` → pause/resume game |
| `DA_GameTagRegistry` (01) | `Function Call` | Register `Framework.Platform.{Name}` tag |
---
## 9. Platform Enum Mapping (Deprecation of old enums)
The old scattered platform enums are mapped to `EPlatformFamily` as follows:
| Deprecated Enum | Old Value | EPlatformFamily |
|----------------|-----------|-----------------|
| `E_InputPlatform::PC_KeyboardMouse` | 0 | `PC_Win64` (or `PC_Linux`, `Mac`) |
| `E_InputPlatform::Xbox` | 1 | `Xbox_Series` or `Xbox_One` (detected) |
| `E_InputPlatform::PS5_DualSense` | 2 | `PS5` or `PS5_Pro` |
| `EControllerPlatform::PC_Generic` | 1 | `PC_Win64` |
| `EControllerPlatform::Xbox` | 2 | `Xbox_Series` or `Xbox_One` |
| `EControllerPlatform::PS5_DualSense` | 3 | `PS5` |
| `EControllerPlatform::PS4_DualShock` | 4 | `PS4` |
All systems that previously used their own platform enum now call:
```
BPC_PlatformServiceAbstraction → GetPlatformFamily() → map to local enum internally
```
---
## 10. Validation / Testing Checklist
- [ ] `DetectPlatform` correctly identifies all 12 platform families
- [ ] `OverridePlatform = PS4` → all systems use PS4 profiles (baked lighting, DualShock, PSN SDK)
- [ ] `OverridePlatform = Switch` → all systems use Switch profiles (baked, NIS, Joy-Con, Nintendo SDK)
- [ ] `OverridePlatform = PS5_Pro` → RenderPipelineManager enables PSSR upscaler
- [ ] `GetDefaultInputProfile` returns correct input profile for each platform
- [ ] `SubmitAchievementToPlatform` on Steam build successfully calls Steam API
- [ ] `SubmitAchievementToPlatform` in Editor returns true (no-op stub, no crash)
- [ ] `SyncCloudSave` routes to correct platform cloud API
- [ ] `ShowPlatformOverlay(Achievements)` opens correct platform overlay
- [ ] `OnPlatformReady` fires before any dependent system queries `GetPlatformFamily()`
- [ ] Changing `OverridePlatform` at runtime (dev cheat) cascades to all bound systems
- [ ] Edge case: No SDK available (editor, generic build) → all SDK calls return no-op stubs
- [ ] Edge case: Platform SDK initialization fails → `CurrentSDK = None`, systems fallback gracefully
- [ ] Edge case: Steam Deck detection in Desktop Mode → reported as `PC_Linux`, not `SteamDeck`
- [ ] Edge case: Xbox Series S (Lockhart) vs Series X (Anaconda) → different capabilities for GPU
---
## 11. Manual Implementation Guide
### 11.1 Class Setup
1. Create Blueprint Class: parent `ActorComponent`, name `BPC_PlatformServiceAbstraction`
2. Path: `Content/Framework/Core/`
3. Attach to `GI_GameFramework` (or BP child of it)
### 11.2 Key UE5 Nodes
| Node | Where to Find | Used For |
|------|---------------|----------|
| `Get Platform Name` | Right-click → "Get Platform Name" | Primary detection API |
| `Switch on String` | Right-click → "Switch" | Platform name branching |
| `Is Steam Deck` | Custom node / check for "SteamDeck" in device name | Steam Deck detection |
| `Is In Editor` | Right-click → "Is In Editor" | SDK substitution in editor |
| `Switch on EPlatformFamily` | Right-click → "Switch" | Platform-specific logic |
### 11.3 Node-by-Node: DetectPlatform
```
[Function: DetectPlatform]
Step 1: Get Platform Name → Store as PlatformStr
Step 2: Switch on String (PlatformStr):
"PS5" →
Step 2a: Get device model info → if "Pro" → PS5_Pro, else PS5
"PS4" → PS4
"XboxOne" → Xbox_One
"XSX" → Xbox_Series (check if "Lockhart" variant for Series S)
"XboxAnaconda" → Xbox_Series
"Switch" → Switch (check model revision for Switch_2)
"Win64" →
Step 2b: Check if running on Steam Deck → if yes → SteamDeck
Step 2c: Else → PC_Win64
"Linux" → PC_Linux
"Mac" → Mac
Default → PC_Win64 (fallback)
Step 3: Store result in CurrentPlatform
Step 4: Return CurrentPlatform
```
### 11.4 Networking
- **Platform is per-client.** Each client may be on a different platform (PC host + PS5 client).
- `BPC_PlatformServiceAbstraction` runs on ALL instances (server + each client).
- Platform SDK calls are local-to-client.
- For listen server: host's platform SDK is used for host's achievements; clients use their own.
### 11.5 Multiplayer Note
- In a multiplayer session, each player's platform is detected independently
- Cross-platform play: a PC host may have Steam SDK active while a PS5 client has PSN SDK
- The Game Instance's PlatformServiceAbstraction only represents the LOCAL player's platform
- For dedicated servers: `CurrentPlatform = PC_Linux` or `PC_Win64`, `CurrentSDK = None` (no achievements on dedicated server)
---
## 12. Blueprint Build Checklist
- [ ] Create Blueprint class: `BPC_PlatformServiceAbstraction` (parent: `ActorComponent`)
- [ ] Add enums: `EPlatformFamily` (12 values), `EPlatformSDK` (6 values), `EPlatformOverlayType` (5 values)
- [ ] Add struct: `SPlatformCapabilities`
- [ ] Add all variables from Section 3
- [ ] Build `Initialize``DetectPlatform``BuildCapabilities` → broadcast chain
- [ ] Implement `DetectPlatform` with Switch on GetPlatformName
- [ ] Implement `SubmitAchievementToPlatform` with Switch on CurrentSDK
- [ ] Implement `SyncCloudSave`, `ShowPlatformOverlay`
- [ ] Implement all boolean convenience functions (`IsConsole`, `IsPS5`, etc.)
- [ ] Create all 7 event dispatchers
- [ ] Wire OnPlatformReady as deferred init trigger for all dependent subsystems
- [ ] Test: OverridePlatform = PS5 → OnPlatformReady fires with PS5
- [ ] Test: Editor mode → SDK calls return no-op stubs
- [ ] Test: Steam build → SubmitAchievement calls Steam API
- [ ] Test: Platform hot-swap (dev cheat) cascades to all bound systems
---
*Blueprint Spec: Platform Service Abstraction. Conforms to TEMPLATE.md v2.0 — part of the UE5 Modular Game Framework, CORE layer.*