# 150 — Platform Service Abstraction (`BPC_PlatformServiceAbstraction`)

> **Blueprint-Only Implementation** — UE 5.5–5.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.*
