반응형

안녕하세요

unity cloud 연결을 위해 Apple Gamekit Plugin을 사용해보았지만

여러 이슈들로 인해 현재 이용이 어려워서 우회하는 방법을 이전 포스팅에 소개해드렸는데요

https://horae.tistory.com/1124

 

Unity Cloud Autenticator Game Center 연동시 발생하는 에러 해결

안녕하세요 오늘은 unity cloud authenticator Gamecenter 연동 과정중에 잘 안되는 부분을 해결하고자 하였고 어느정도 우회 방법을 찾았습니다. 우선 Unity에서는 Apple.gamekit을 이용한 연동 방식을 가이드

horae.tistory.com

오늘은 세부 설정 방법에 대하여 소개해드리려고 해요 

1. Asset/Plugin/iOS 폴더 밑에 .m 파일 생성 한다.

구체적으로 c#스크립트를 먼저 생성 후 확장자 이름을 .m으로 변경해줘야 한다. (finder에서 변경) 

변경 이후 기존 c# .meta 파일은 삭제

 

.m 파일에 아래 코드 삽입

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>

typedef void (*IdentityVerificationSignatureCallback)(const char * publicKeyUrl, const char * signature, int signatureLength, const char * salt, int saltLength, const uint64_t timestamp, const char * error);

extern void generateIdentityVerificationSignature(IdentityVerificationSignatureCallback callback) {
    
    GKLocalPlayer * localPlayer = [GKLocalPlayer localPlayer];

    NSLog(@"LocalPlayer: %@", localPlayer.playerID);
    
    [localPlayer fetchItemsForIdentityVerificationSignature:^(NSURL * _Nullable publicKeyURL, NSData * _Nullable signature, NSData * _Nullable salt, uint64_t timestamp, NSError * _Nullable error) {

        NSLog(@"Received 'generateIdentityVerificationSignature' callback, error: %@", error.description);

        // Create a pool for releasing the resources we create
        @autoreleasepool {
            
            // PublicKeyUrl
            const char * publicKeyUrlCharPointer = NULL;
            if (publicKeyURL != NULL)
            {
                const NSString * publicKeyUrlString = [[NSString alloc] initWithString:[publicKeyURL absoluteString]];
                publicKeyUrlCharPointer = [publicKeyUrlString UTF8String];
            }


            // Signature
            const char * signatureBytes = (char*)[signature bytes];
            int signatureLength = (int)[signature length];

            // Salt
            const char * saltBytes = (char*)[salt bytes];
            int saltLength = (int)[salt length];

            // Error
            const NSString * errorString = error.description;
            const char * errorStringPointer = [errorString UTF8String];

            // Callback
            callback(publicKeyUrlCharPointer, signatureBytes, signatureLength, saltBytes, saltLength, timestamp, errorStringPointer);
        }
    }];
}

 

 

2. 위에서 만든 코드를 실제 호출하는 코드 이며, .cs파일에 넣는다. 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void IdentityVerificationSignatureCallback(
        string publicKeyUrl, 
        IntPtr signaturePointer, int signatureLength,
        IntPtr saltPointer, int saltLength,
        ulong timestamp,
        string error);

    [DllImport("__Internal")]
    public static extern void generateIdentityVerificationSignature(
        [MarshalAs(UnmanagedType.FunctionPtr)]IdentityVerificationSignatureCallback callback);

// Note: This callback has to be static because Unity's il2Cpp doesn't support marshalling instance methods.
    [MonoPInvokeCallback(typeof(IdentityVerificationSignatureCallback))]
    public static void OnIdentityVerificationSignatureGenerated(
        string publicKeyUrl, 
        IntPtr signaturePointer, int signatureLength,
        IntPtr saltPointer, int saltLength,
        ulong timestamp,
        string error)
    {
        // Create a managed array for the signature
        var signature = new byte[signatureLength];
        Marshal.Copy(signaturePointer, signature, 0, signatureLength);

        // Create a managed array for the salt
        var salt = new byte[saltLength];
        Marshal.Copy(saltPointer, salt, 0, saltLength);

        UnityEngine.Debug.Log($"publicKeyUrl: {publicKeyUrl}");
        UnityEngine.Debug.Log($"signature: {signature.Length}");
        UnityEngine.Debug.Log($"salt: {salt.Length}");
        UnityEngine.Debug.Log($"timestamp: {timestamp}");
        UnityEngine.Debug.Log($"error: {error}");
    }

 

3. 해당 코드를 호출 및 사용 하도록 코드 구성

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void IdentityVerificationSignatureCallback(
        string publicKeyUrl,
        IntPtr signaturePointer, int signatureLength,
        IntPtr saltPointer, int saltLength,
        ulong timestamp,
        string error);

    [DllImport("__Internal")]
    public static extern void generateIdentityVerificationSignature(
        [MarshalAs(UnmanagedType.FunctionPtr)] IdentityVerificationSignatureCallback callback);

    // Note: This callback has to be static because Unity's il2Cpp doesn't support marshalling instance methods.
    [MonoPInvokeCallback(typeof(IdentityVerificationSignatureCallback))]
    public static void OnIdentityVerificationSignatureGenerated(
        string publicKeyUrl,
        IntPtr signaturePointer, int signatureLength,
        IntPtr saltPointer, int saltLength,
        ulong timestamp,
        string error)
    {
        // Create a managed array for the signature
        var signature = new byte[signatureLength];
        Marshal.Copy(signaturePointer, signature, 0, signatureLength);

        // Create a managed array for the salt
        var salt = new byte[saltLength];
        Marshal.Copy(saltPointer, salt, 0, saltLength);


        string time = timestamp.ToString();
        UnityEngine.Debug.Log($"horae:: publicKeyUrl: {publicKeyUrl}");
        UnityEngine.Debug.Log($"horae:: signature.length: {signature.Length}");
        UnityEngine.Debug.Log($"horae:: salt.length: {salt.Length}");
        UnityEngine.Debug.Log($"horae:: string timestamp: {time}");
        UnityEngine.Debug.Log($"horae:: ulong timestamp: {timestamp}");
        UnityEngine.Debug.Log($"horae:: error: {error}");
        UnityEngine.Debug.Log($"horae:: signature: {BitConverter.ToString(signature).Replace("-", "")}");
        UnityEngine.Debug.Log($"horae:: salt: {BitConverter.ToString(salt).Replace("-", "")}");
        UnityEngine.Debug.Log($"horae:: signature base64: {Convert.ToBase64String(signature)}");
        UnityEngine.Debug.Log($"horae:: salt base64: {Convert.ToBase64String(salt)}");
        Task task = SignInWithAppleGameCenterAsync(Convert.ToBase64String(signature), Social.localUser.id, publicKeyUrl, Convert.ToBase64String(salt), timestamp);
    }


    public void StartIdentityVerification()
    {
        // 네이티브 코드에게 인증 서명을 생성하도록 요청합니다.
        generateIdentityVerificationSignature(OnIdentityVerificationSignatureGenerated);

   

    }


    public static async Task SignInWithAppleGameCenterAsync(string signature, string teamPlayerId, string publicKeyURL, string salt, ulong timestamp)
    {

        Debug.Log("horae:: SignInWithAppleGameCenterAsync");
        Debug.Log("horae:: SignInWithAppleGameCenterAsync signature: " + signature);
        Debug.Log("horae:: SignInWithAppleGameCenterAsync teamPlayerId: " + teamPlayerId);
        Debug.Log("horae:: SignInWithAppleGameCenterAsync publicKeyURL: " + publicKeyURL);
        Debug.Log("horae:: SignInWithAppleGameCenterAsync salt: " + salt);
        Debug.Log("horae:: SignInWithAppleGameCenterAsync timestamp: " + timestamp);
        try
        {
            Debug.Log("horae:: try await SignInWithAppleGameCenterAsync");
            await AuthenticationService.Instance.SignInWithAppleGameCenterAsync(signature, teamPlayerId, publicKeyURL, salt, timestamp);
            UnityEngine.Debug.Log("horae:: SignIn is successful.");
        }
        catch (AuthenticationException ex)
        {
            // Compare error code to AuthenticationErrorCodes
            // Notify the player with the proper error message
            Debug.Log("horae:: AuthenticationException");
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex)
        {
            // Compare error code to CommonErrorCodes
            // Notify the player with the proper error message
            Debug.Log("horae:: RequestFailedException");
            Debug.LogException(ex);
        }
    }

 

4. 이제 StartIdentityVerification() 함수를 호출해주면 Gamecenter 로 로그인한 유저를

Unity Cloud로 연결해준다.

 

AppleGamecenterHandler.Instance.GameCenterInit();
AppleGamecenterHandler.Instance.StartIdentityVerification();

 

GamecenterInit()은 아래와 같이 구성되어 있다

 public void GameCenterInit()
    {
        DontDestroyOnLoad(gameObject);
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            if (task.Exception != null)
            {
                Debug.LogError($"Failed to initialize Firebase with {task.Exception}");
                return;
            }

            auth = FirebaseAuth.DefaultInstance;

            OnFirebaseInitialized.Invoke();
        });

        GameCenterLogin();
    }

    /// <summary>
    /// Apple GameCenter Login
    /// </summary>
    public void GameCenterLogin()
    {





        if (Social.localUser.authenticated == true)
        {
            Debug.Log("Success to true");
        }
        else
        {
            Social.localUser.Authenticate((bool success) =>
            {
                if (success)
                {
                    Debug.Log("Success to authenticate");
                }
                else
                {
                    Debug.Log("Faile to login");
                }
            });
        }
    }

 

5. Unity Cloud 연동 확인

반응형

+ Recent posts