Skip to content

Commit

Permalink
Fix multiple bugs in GKLocalPlayer.FetchItems. Also test it during au…
Browse files Browse the repository at this point in the history
…thentication in the sample app. (apple#30)
  • Loading branch information
AdamSzApple authored and GitHub Enterprise committed Feb 23, 2024
1 parent 676672b commit 73e5e56
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Apple.Core;
using Apple.Core.Runtime;
using Apple.GameKit.Leaderboards;
Expand Down Expand Up @@ -151,9 +152,22 @@ private async void OnAuthenticate()
_isMultiplayerGamingRestrictedText.text = $"IsMultiplayerGamingRestricted: {_localPlayer.IsMultiplayerGamingRestricted}";
_isPersonalizedCommunicationRestrictedText.text = $"IsPersonalizedCommunicationRestricted: {_localPlayer.IsPersonalizedCommunicationRestricted}";
_isUnderageText.text = $"IsUnderage: {_localPlayer.IsUnderage}";

await TestFetchItems();
}
}

private async Task TestFetchItems()
{
var items = await GKLocalPlayer.Local.FetchItems();
Debug.Log(
"GKLocalPlayer.FetchItems:\n" +
$" PublicKeyUrl={items.PublicKeyUrl}\n" +
$" Signature={Convert.ToBase64String(items.GetSignature())} ({items.Signature.Length} bytes)\n" +
$" Salt={Convert.ToBase64String(items.GetSalt())} ({items.Salt.Length} bytes)\n" +
$" Timestamp={items.Timestamp}\n");
}

private static Stack<GameObject> _panelStack;
private static Stack<GameObject> PanelStack => _panelStack ??= new Stack<GameObject>();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,31 @@
using System;
using System.Runtime.InteropServices;
using Apple.Core.Runtime;

namespace Apple.GameKit.Players
{
[StructLayout(LayoutKind.Sequential)]
public struct GKIdentityVerificationResponse
public class GKIdentityVerificationResponse
{
/// <summary>
/// The URL for the public encryption key.
/// </summary>
public string PublicKeyUrl;
internal IntPtr Signature;
internal int SignatureLength;
internal IntPtr Salt;
internal int SaltLength;
/// <summary>
/// The signature’s creation date and time.
/// </summary>
public ulong Timestamp;
public string PublicKeyUrl { get; set; }

/// <summary>
/// The verification signature data that GameKit generates.
/// </summary>
/// <returns></returns>
public byte[] GetSignature()
{
var signature = new byte[SignatureLength];
Marshal.Copy(Signature, signature, 0, SignatureLength);

return signature;
}
public NSData Signature { get; set; }
public byte[] GetSignature() => Signature.Bytes;

/// <summary>
/// A random NSString that GameKit uses to compute the hash and randomize it.
/// </summary>
/// <returns></returns>
public byte[] GetSalt()
{
var salt = new byte[SaltLength];
Marshal.Copy(Salt, salt, 0, SaltLength);
public NSData Salt { get; set; }
public byte[] GetSalt() => Salt.Bytes;

return salt;
}
/// <summary>
/// The signature's creation date and time.
/// </summary>
public UInt64 Timestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,26 @@ public Task<GKIdentityVerificationResponse> FetchItems()
return tcs.Task;
}

[MonoPInvokeCallback(typeof(SuccessTaskCallback<GKIdentityVerificationResponse>))]
private static void OnFetchItems(long taskId, GKIdentityVerificationResponse response)
internal delegate void InternalOnFetchItemsHandler(long taskId, string publicKeyUrl, IntPtr signaturePtr, IntPtr saltPtr, UInt64 timestamp);

[MonoPInvokeCallback(typeof(InternalOnFetchItemsHandler))]
private static void OnFetchItems(long taskId, string publicKeyUrl, IntPtr signaturePtr, IntPtr saltPtr, UInt64 timestamp)
{
InteropTasks.TrySetResultAndRemove(taskId, response);
try
{
var response = new GKIdentityVerificationResponse
{
PublicKeyUrl = publicKeyUrl,
Signature = PointerCast<NSData>(signaturePtr),
Salt = PointerCast<NSData>(saltPtr),
Timestamp = timestamp
};
InteropTasks.TrySetResultAndRemove(taskId, response);
}
catch (Exception ex)
{
InteropTasks.TrySetExceptionAndRemove<GKIdentityVerificationResponse>(taskId, ex);
}
}

[MonoPInvokeCallback(typeof(NSErrorTaskCallback))]
Expand Down Expand Up @@ -233,7 +249,7 @@ private static class Interop
[DllImport(InteropUtility.DLLName)]
public static extern IntPtr GKLocalPlayer_GetLocal();
[DllImport(InteropUtility.DLLName)]
public static extern void GKLocalPlayer_FetchItems(IntPtr pointer, long taskId, SuccessTaskCallback<GKIdentityVerificationResponse> onSuccess, NSErrorTaskCallback onError);
public static extern void GKLocalPlayer_FetchItems(IntPtr pointer, long taskId, InternalOnFetchItemsHandler onSuccess, NSErrorTaskCallback onError);
[DllImport(InteropUtility.DLLName)]
public static extern void GKLocalPlayer_Authenticate(long taskId, SuccessTaskCallback<IntPtr> onSuccess, NSErrorTaskCallback onError);
[DllImport(InteropUtility.DLLName)]
Expand Down
21 changes: 13 additions & 8 deletions plug-ins/Apple.GameKit/Native/GameKitWrapper/GKLocalPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -226,30 +226,35 @@ public func GKLocalPlayer_LoadFriendsAuthorizationStatus
};
}

public typealias SuccessTaskFetchItemsCallback = @convention(c) (Int64, char_p, uchar_p, Int32, uchar_p, Int32, UInt) -> Void;
public typealias SuccessTaskFetchItemsCallback = @convention(c) (
Int64 /*taskId*/,
char_p /*publicKeyUrl*/,
UnsafeMutableRawPointer /*signatureData*/,
UnsafeMutableRawPointer /*saltData*/,
UInt64 /*timestamp*/) -> Void;

@_cdecl("GKLocalPlayer_FetchItems")
public func GKLocalPlayer_FetchItems
(
gkLocalPlayerPtr: UnsafeMutableRawPointer,
taskId: Int64,
onSuccess: @escaping SuccessTaskFetchItemsCallback,
onError: @escaping NSErrorCallback
)
{
if #available(macOS 10.15.5, iOS 13.5, tvOS 13.5, *) {
GKLocalPlayer.local.fetchItems(forIdentityVerificationSignature: { publicKeyUrl, signature, salt, timestamp, error in
let player = Unmanaged<GKLocalPlayer>.fromOpaque(gkLocalPlayerPtr).takeUnretainedValue();
player.fetchItems(forIdentityVerificationSignature: { publicKeyUrl, signature, salt, timestamp, error in
if (error != nil) {
onError(taskId, Unmanaged.passRetained(error! as NSError).toOpaque());
return;
}

onSuccess(taskId,
publicKeyUrl!.absoluteString.toCharPCopy(),
signature!.toUCharP(),
Int32(signature!.count),
salt!.toUCharP(),
Int32(salt!.count),
UInt(timestamp));
Unmanaged.passRetained(signature! as NSData).toOpaque(),
Unmanaged.passRetained(salt! as NSData).toOpaque(),
UInt64(timestamp));
})
} else {
let error = NSError.init(domain: "GKLocalPlayer", code: GKErrorCodeExtension.unsupportedOperationForOSVersion.rawValue, userInfo: nil);
Expand Down

0 comments on commit 73e5e56

Please sign in to comment.