Player Data and Game Servers #

Game servers will need access to specific player data. Servers should not use the get all API to fetch player data. Implement operations that return the specific data needed by the game server.

Remember the Partner SDK includes all operations that have the SessionType.PARTNER in the operation’s annotation.

Example operation #

Below is a basic example of an operation that returns loadout data.

package documentation

import pragma.playerdata.Component
import pragma.playerdata.Context
import pragma.playerdata.Entity
import pragma.playerdata.FieldNumber
import pragma.playerdata.PlayerDataContentLibrary
import pragma.playerdata.PlayerDataOperation
import pragma.playerdata.PlayerDataRequest
import pragma.playerdata.PlayerDataResponse
import pragma.playerdata.PlayerDataSnapshot
import pragma.playerdata.PlayerDataSubService
import pragma.rpcs.SessionType
import pragma.types.SerializedName
import pragma.utils.ComponentId

class GetLoadoutRequest : PlayerDataRequest
data class GetLoadoutResponse(
    val selectedCharacter: String,
    val equippedGear: List<EquippedGear>,
) : PlayerDataResponse
data class EquippedGear(
    val gearId: String,
    val skinId: String,
)

@Suppress("unused") // used by reflection
class LoadoutSubService(contentLibrary: PlayerDataContentLibrary) : PlayerDataSubService(contentLibrary) {

    // Define operations to fetch data game-servers require.
    // The Partner SDK does not cache any player data.
    @PlayerDataOperation(sessionTypes = [SessionType.PARTNER])
    fun getLoadout(
        @Suppress("UNUSED_PARAMETER") request: GetLoadoutRequest,
        context: Context
    ): GetLoadoutResponse {
        val inventory = context.snapshot.getGearInventory()
        val loadout = context.snapshot.getOrCreateLoadout()
        // filter and collect the data wanted by the game-server
        val equippedGear = loadout.equippedGear.mapNotNull {
            val component = inventory.getComponentById<Gear>(it) ?: return@mapNotNull null
            EquippedGear(component.gearId, component.skinId)
        }
        return GetLoadoutResponse(loadout.heroSelected, equippedGear)
    }

    // entity to hold all of a player's gear items
    private fun PlayerDataSnapshot.getGearInventory(): Entity {
        return getOrCreateUniqueEntity("GearInventory")
    }

    // entity to store a player's loadout
    private fun PlayerDataSnapshot.getOrCreateLoadout(): Loadout {
        return getOrCreateUniqueEntity("Loadout") {
            listOf(Loadout(mutableListOf(), ""))
        }.getComponentsByType<Loadout>().single()
    }
}

// example of component data
@SerializedName("1759966426")
data class Loadout(
    @FieldNumber(1) var equippedGear: MutableList<ComponentId>,
    @FieldNumber(2) var heroSelected: String,
) : Component

@SerializedName("1759966432")
data class Gear(
    @FieldNumber(1) var gearId: String,
    @FieldNumber(2) var minDamage: Int,
    @FieldNumber(3) var maxDamage: Int,
    @FieldNumber(4) var skinId: String,
) : Component

Call using the Unreal Partner SDK #

Below is an example of using the Unreal Partner SDK to call getLoadout.

void AUnicornGameMode::GetLoadout(const FString &PlayerId) {
  // providing type - assign as private property on the GameMode etc ...
  const Pragma::FServerPtr PartnerSdk; 

  PartnerSdk->PlayerDataService().Loadout().GetLoadout(
      PlayerId,
      FOnLoadoutGetLoadoutDelegate::CreateLambda(
          [](const TPragmaResult<FPragma_PlayerData_GetLoadoutResponseProto>
                 &Result) {
            if (Result.IsSuccessful()) {
              const auto PlayersLoadout =
                  Result.Payload<FPragma_PlayerData_GetLoadoutResponseProto>();
              UE_LOG(LogTemp, Display, TEXT("Current character from load: %s"),
                     *PlayersLoadout.SelectedCharacter);

            } else {
              // failure occurred ... 
            }
          }));
}