PlayFabMultiplayer.psm1
Set-StrictMode -Version Latest <# .SYNOPSIS Internal helper to call a playFab API, wait for its task to complete, and handle some boilerplate error checking/logging of failures. Returns the API response (the PlayFabResult<T>.Result obj) on success; throws on errors. .PARAMETER ApiName Used for logging; typically the playfab method name (eg: ListQosServersAsync) .PARAMETER ApiCall The api call in a script block; expected to return a Task with a playfab result. eg: { [PlayFab.PlayFabMultiplayerAPI]::ListQosServersAsync($null) } .PARAMETER SkipTokenCheck Optional. Set this switch to bypass the check for an entity token. Typically only used for the entity token API call; all other PlayFab APIs require a token. .PARAMETER WarnIfPaged Optional. If set, we check for a NextLink field on a successful response and warn if the results have a next link (paged results). #> function CallPlayFabApi { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string] $ApiName, [Parameter(Mandatory = $true)] [scriptblock] $ApiCall, [Parameter(Mandatory = $false)] [switch] $SkipTokenCheck = $false, [Parameter(Mandatory = $false)] [switch] $WarnIfPaged = $false # most calls aren't paged in the first place ) # login / entity token check if (-not $SkipTokenCheck -and [string]::IsNullOrEmpty([PlayFab.PlayFabSettings]::TitleId)) { $msg = "Call Get-PFTitleEntityToken first to setup your credentials for a PlayFab title." Write-Warning $msg throw "PlayFabSettings doesn't have a configured titleId; $msg" } # send/wait for API response $apiTask = & $ApiCall $apiTask.Wait() if ($apiTask.IsFaulted) { $aggregateEx = $apiTask.Exception.Flatten() $errMsg = "PlayFab API $ApiName failed with exceptions:" foreach ($ex in $aggregateEx.InnerExceptions) { $errMsg += $ex.Message } Write-Warning $errMsg throw $ex } # boilerplate error handling $playFabResult = $apiTask.Result if ($playFabResult.Error -ne $null) { $pfError = $PlayFabResult.Error $errReport = $pfError.GenerateErrorReport() $errMsg = "PlayFab API $ApiName returned Http StatusCode $($pfError.HttpCode) ($($pfError.HttpStatus)); ErrorMsg: $($pfError.ErrorMessage)." Write-Warning "$errMsg `n PlayFab Error Report: $errReport" throw $errMsg } $result = $PlayFabResult.Result if ($WarnIfPaged -and $result.SkipToken -ne $null) { Write-Warning "There are many results requiring paging, this operation may take some time." } return $result } function Get-PFMultiplayerQosServer { [CmdletBinding( )] Param() Begin {} Process { $qosServersResult = CallPlayFabApi -ApiName ListQosServersAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::ListQosServersAsync($null) } return $qosServersResult.QosServers } <# .SYNOPSIS Gets a list of QoS servers. .DESCRIPTION Returns list of QoS servers to use for network performance measurements and region selection during allocation. #> } function Get-PFTitleEntityToken { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)][string]$TitleID, [Parameter(Mandatory = $true)][string]$SecretKey ) Begin {} Process { #Sets the public properties of the API's static class that configure which title target via entity tokens [PlayFab.PlayFabSettings]::TitleID = $TitleID [PlayFab.PlayFabSettings]::DeveloperSecretKey = $SecretKey $key = new-object PlayFab.AuthenticationModels.EntityKey $key.ID = $TitleID $tokenRequest = new-object PlayFab.AuthenticationModels.GetEntityTokenRequest $tokenRequest.Entity = $key $entityTokenResult = CallPlayFabApi -ApiName GetEntityTokenAsync -ApiCall { [PlayFab.PlayFabAuthenticationAPI]::GetEntityTokenAsync($tokenRequest) } -SkipTokenCheck return $entityTokenResult } <# .SYNOPSIS Gets an entity token using the provided title and secret key. Required for other Entity API interactions. .DESCRIPTION Using a secret key generated in Game Manager, this cmdlet generates and entity token for the specified title id. The entity token will be used for authenticating other PlayCompute cmdlets for the length of the PowerShell session. #> } function Get-PFMultiplayerAsset { [CmdletBinding( )] Param( [Parameter(Mandatory = $false)][string] $SkipToken) Begin {} Process { $assetReq = NEW-OBJECT PlayFab.MultiplayerModels.ListAssetsRequest if ($SkipToken -ne "") { $assetReq.SkipToken = $SkipToken $assetReq.NumItems = 10 } $assetsResult = CallPlayFabApi -ApiName ListAssetsAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::ListAssetsAsync($assetReq) } -WarnIfPaged $SkipToken = $assetsResult.SkipToken if ($SkipToken -ne "") { write-progress "Getting more assets" $newresult = Get-PFMultiplayerAsset -SkipToken $SkipToken $result = $assetsResult.AssetSummaries + $newresult return $result } return $assetsResult.AssetSummaries } <# .SYNOPSIS Gets the game server assets that have been uploaded .DESCRIPTION Gets the game server assets that have been added through Add-PFMultiplayerAssets or GetAssetUploadURL API. Command is run in the context of the title specified using Get-PFTitleEntityToken #> } function Add-PFMultiplayerAsset { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)] [string] $FilePath ) Begin {} Process { if (-not (Test-Path $FilePath)) { write-error "Provided file path is not valid" return } $assetFileItem = Get-Item $FilePath $assetHash = Get-FileHash $FilePath -Algorithm MD5 $assetReq = NEW-OBJECT PlayFab.MultiplayerModels.GetAssetUploadUrlRequest $BlobName = $FilePath | Split-Path -Leaf $assetReq.Name = $BlobName $metadata = New-Object 'System.Collections.Generic.Dictionary[String,String]' $metadata.Add("OriginalFilePath",$FilePath) $metadata.Add("sizeBytes", $assetFileItem.Length) $metadata.Add("md5", $assetHash.Hash) $metadata.Add("uploadTimeUtc", [DateTime]::UtcNow.ToString()) $assetReq.MetaData = $MetaData $assetsResult = CallPlayFabApi -ApiName GetAssetUploadUrlAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::GetAssetUploadUrlAsync($assetReq) } $sastoken = $assetsResult.AssetUploadUrl $sastoken = $sastoken.Remove($sastoken.LastIndexOf("&api")) $storageaccountname = $sastoken.SubString(8,$sastoken.IndexOf("blob")-9) $sastoken = $sastoken.substring($sastoken.IndexOf("sv")) $accountContext = New-AzureStorageContext -SasToken $sasToken -StorageAccountName $storageaccountname ## $blob = Get-AzureStorageBlob -Container "gameassets" -Blob $ID -Context $accountContext Set-AzureStorageBlobContent -File $FilePath -Container "gameassets" -Context $accountContext -Blob $BlobName } <# .SYNOPSIS Uploads an asset to PlayFab. .DESCRIPTION Upload an asset (commonly a zip file) by providing a friendly name and file path. This cmdlet uses the GetAssetUploadURl API to get an Azure blob URL, and then uses Azure storage cmdlets to upload the asset. #> } function Add-PFMultiplayerCertificate { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)][string]$Name, [Parameter(Mandatory = $true)][string]$FilePath, [Parameter(Mandatory = $false)][string]$Password ) Begin {} Process { $PathTest = Test-Path $FilePath if ($PathTest -eq $false) { write-error "Provided file path is not valid" return } $certificateBytes = [System.IO.File]::ReadAllBytes($FilePath) $base64 = [System.Convert]::ToBase64String($certificateBytes) $cert = NEW-OBJECT PlayFab.MultiplayerModels.Certificate $cert.Base64EncodedValue = $base64 $cert.Name = $Name if($Password -ne $null) { $cert.Password = $Password } $certReq = NEW-OBJECT PlayFab.MultiplayerModels.UploadCertificateRequest $certReq.GameCertificate = $cert return CallPlayFabApi -ApiName UploadCertificateAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::UploadCertificateAsync($certReq) } } <# .SYNOPSIS Uploads a certificate to PlayFab. .DESCRIPTION Uploads a certificate to PlayFab for game server usage. Cmdlet does not support certificates with passwords but coming soon. #> } function Get-PFMultiplayerCertificate { [CmdletBinding( )] Param() Begin {} Process { $listCertResult = CallPlayFabApi -ApiName ListCertificatesAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::ListCertificatesAsync($null) } return $listCertResult.CertificateSummaries } <# .SYNOPSIS Gets the game server certificates that have been uploaded .DESCRIPTION Gets the game server Certificate that have been added through Add-PFMultiplayerCertificate. #> } function Get-PFMultiplayerBuild { [CmdletBinding( DefaultParameterSetName="All")] Param( [Parameter(ParameterSetName = "SpecificName", Mandatory = $true)][string]$BuildName, [Parameter(ParameterSetName = "All")][Switch]$All ) Begin {} Process { $buildsResult = CallPlayFabApi -ApiName ListBuildsAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::ListBuildsAsync($null) } if ($PSCmdlet.ParameterSetName -eq "All") { return $buildsResult.BuildSummaries } return $buildsResult.BuildSummaries | Where-Object -Property BuildName -Contains $BuildName } <# .SYNOPSIS Gets the game server builds that have been created .DESCRIPTION Gets the game server builds #> } function New-PFMultiplayerBuild { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)][string]$BuildName, [Parameter(Mandatory = $true)][string]$AssetName, [Parameter(Mandatory = $true)][string]$AssetMountPath, [Parameter(Mandatory = $true)][string]$StartGameCommand, [Parameter(Mandatory = $true)]$MappedPorts, [Parameter(Mandatory = $true)]$VMSize, [Parameter(Mandatory = $true)]$BuildCerts ) Begin {} Process { $BuildReq = NEW-OBJECT PlayFab.MultiplayerModels.CreateBuildWithManagedContainerRequest $BuildReq.BuildName = $BuildName $BuildReq.ContainerFlavor = [PlayFab.MultiplayerModels.ContainerFlavor]::WindowsServerCorePlayFab $BuildReq.StartGameCommand = $StartGameCommand $BuildReq.MappedPorts = $MappedPorts $BuildReq.VMSize = $VMSize $BuildReq.GameCertificateReferences = $BuildCerts $BuildReq.MultiplayerServerCountPerVm = 1 $Asset = NEW-OBJECT PlayFab.MultiplayerModels.AssetReferenceParams $Asset.Name = $AssetName $Asset.MountPath = $AssetMountPath $BuildReq.GameAssetReferences = $Asset $Regions = NEW-OBJECT PlayFab.MultiplayerModels.BuildRegionParams $Regions.MaxSessions = 50 $Regions.Region = [PlayFab.MultiplayerModels.AzureRegion]::EastUs $Regions.StandbySessions = 2 $BuildReq.RegionConfiguration = $Regions $metadata = New-Object 'System.Collections.Generic.Dictionary[String,String]' $metadata.Add("CreatedBy","PowerShell") $BuildReq.MetaData = $MetaData $buildResult = CallPlayFabApi -ApiName CreateBuildWithManagedContainerAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::CreateBuildWithManagedContainerAsync($BuildReq) } return $buildResult.BuildSummary } <# .SYNOPSIS Creates a game server build. .DESCRIPTION Creates a game server build. Currently hard-coded to create a Windows Server Core build. .EXAMPLE $VMSelection = [PlayFab.MultiplayerModels.AzureVMSize]::Standard_F4 $Ports = New-object PlayFab.MultiplayerModels.Port $Ports.Name = "Test Port" $Ports.Num = 3055 $Ports.Protocol = [PlayFab.MultiplayerModels.ProtocolType]::UDP $BuildCert = New-Object 'System.Collections.Generic.List[String]' $Buildcert.Add("WeirdErrorTest") $Buildcert.Add("FakeCert") New-PFMultiplayerBuild -BuildName "PowerShellTest" -AssetName "HaroRunner" -AssetMountPath "C:\Asset\" -StartGameCommand "C:\Assets\WinTestRunnerGame.exe" -MappedPorts $Ports -VMSize $VMSelection -BuildCerts $BuildCert #> } function Remove-PFMultiplayerBuild { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)][string]$BuildName ) Begin {} Process { $BuildID = Get-PFMultiplayerBuild -BuildName $BuildName $BuildID = $BuildID.BuildID $BuildReq = NEW-OBJECT PlayFab.MultiplayerModels.DeleteBuildRequest $BuildReq.BuildId = $BuildId $result = CallPlayFabApi -ApiName DeleteBuildAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::DeleteBuildAsync($BuildReq) } return $result.BuildSummary } <# .SYNOPSIS Deletes a game server build. .DESCRIPTION Deletes a game server build. .EXAMPLE Remove-PFMultiplayerBuild -BuildId <build_id> #> } function Get-PFMultiplayerServer { [CmdletBinding(DefaultParameterSetName="AllRegions")] Param( [Parameter(Mandatory = $true)][string]$BuildName, [Parameter(ParameterSetName = "SpecificRegion", Mandatory = $True)][PlayFab.MultiplayerModels.AzureRegion]$Region, [Parameter(ParameterSetName = "AllRegions")][Switch] $AllRegions ) Begin {} Process { $BuildID = Get-PFMultiplayerBuild -BuildName $BuildName $BuildID = $BuildID.BuildID $RegionList = New-Object 'System.Collections.Generic.List[PlayFab.MultiplayerModels.AzureRegion]' if($PSCmdlet.ParameterSetName -eq "AllRegions") { $regions = [PlayFab.MultiplayerModels.AzureRegion[]] @("EastUs", "WestUs", "CentralUs", "NorthEurope") $RegionList.AddRange($regions) } else { $RegionList.Add($Region) } foreach ($Region in $RegionList) { $SessionReq = NEW-OBJECT PlayFab.MultiplayerModels.ListMultiplayerServersRequest $SessionReq.BuildID = $BuildID $SessionReq.Region = $Region $result = CallPlayFabApi -ApiName ListMultiplayerServersAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::ListMultiplayerServersAsync($SessionReq) } -WarnIfPaged $unsortedresult = $result.MultiplayerServerSummaries $result = $unsortedresult | Sort-Object region, state return $result } } <# .SYNOPSIS Gets the servers for a build .DESCRIPTION Gets the servers for a build and specified region .Example Get servers from a specific build in East US $Region = [PlayFab.MultiplayerModels.AzureRegion]::EastUS $Name = "ZIP_AllocationTest" Get-PFMultiplayerServer -BuildName $Name -Region $Region .Example Get servers from a specific build across all regions $Name = "ZIP_AllocationTest" Get-PFMultiplayerServer -BuildName $Name -AllRegions #> } function New-PFMultiplayerServer { [CmdletBinding( )] Param( [Parameter(Mandatory = $true)][string]$BuildName, [Parameter(Mandatory = $true)][string]$SessionId, [Parameter(Mandatory = $true)][string]$SessionCookie, [Parameter(Mandatory = $true)][System.Collections.Generic.List[PlayFab.MultiplayerModels.AzureRegion]]$PreferredRegions ) Begin {} Process { $AllocationReq = NEW-OBJECT PlayFab.MultiplayerModels.RequestMultiplayerServerRequest $BuildID = Get-PFMultiplayerBuild -BuildName $BuildName $BuildID = $BuildID.BuildID $AllocationReq.BuildId = $BuildId $AllocationReq.SessionId = $SessionId $AllocationReq.SessionCookie = $SessionCookie $AllocationReq.PreferredRegions = $PreferredRegions return CallPlayFabApi -ApiName RequestMultiplayerServerAsync -ApiCall { [PlayFab.PlayFabMultiplayerAPI]::RequestMultiplayerServerAsync($AllocationReq) } } <# .SYNOPSIS Allocates a new multiplayer server .DESCRIPTION Allocates a new multiplayer server using the specified build and region preferences. .Example $regions = new-object 'System.Collections.Generic.List[PlayFab.MultiplayerModels.AzureRegion]' $regions.Add("EastUS"); New-PFMultiplayerServer -BuildName "MyBuild" -SessionId "00000000-0000-0000-0000-000000000001" -SessionCookie "test cookie" -PreferredRegions $regions #> } ##Export cmdlet and alias to module New-Alias GPFQ Get-PFMultiplayerQosServer Export-ModuleMember -Alias GPFMQ -Function Get-PFMultiplayerQosServer New-Alias GPFTET Get-PFTitleEntityToken Export-ModuleMember -Alias GPFTET -Function Get-PFTitleEntityToken New-Alias GPFGA Get-PFMultiplayerAsset Export-ModuleMember -Alias GPFMA -Function Get-PFMultiplayerAsset New-Alias APFGA Add-PFMultiplayerAsset Export-ModuleMember -Alias APFMA -Function Add-PFMultiplayerAsset New-Alias APFGC Add-PFMultiplayerCertificate Export-ModuleMember -Alias APFMC -Function Add-PFMultiplayerCertificate New-Alias GPFGC Get-PFMultiplayerCertificate Export-ModuleMember -Alias GPFMC -Function Get-PFMultiplayerCertificate New-Alias NPFGB New-PFMultiplayerBuild Export-ModuleMember -Alias NPFMB -Function New-PFMultiplayerBuild New-Alias RPFGB Remove-PFMultiplayerBuild Export-ModuleMember -Alias RPFMB -Function Remove-PFMultiplayerBuild New-Alias GPFGB Get-PFMultiplayerBuild Export-ModuleMember -Alias GPFMB -Function Get-PFMultiplayerBuild New-Alias GPFGSH Get-PFMultiplayerServer Export-ModuleMember -Alias GPFMS -Function Get-PFMultiplayerServer New-Alias NPFGSH New-PFMultiplayerServer Export-ModuleMember -Alias NPFMS -Function New-PFMultiplayerServer |