簡體   English   中英

如何從 TypeScript 中的可調用 Firebase Cloud Function 向我的 Unity 應用程序發送自定義 object?

[英]How can I send custom object from my callable Firebase Cloud Function in TypeScript to my Unity app?

我正在嘗試為我的 Unity 項目使用 Firebase 及其可調用的 Cloud Functions。

通過我在 web 上找到的文檔和不同的帖子,我很難理解返回數據的工作原理。 (本人來自Azure Functions in C#)

我使用 TypeScript,並嘗試返回自定義 object CharactersResponse

export class CharactersResponse //extends CustomResponse
{
    Code!: CharactersCode;
    CharacterID?: string;
}

export enum CharactersCode
{
    Success = 0,
    InvalidName = 2000,
    CharacterNameAlreadyExists = 2009,
    NoCharacterSlotAvailable = 3000,
    InvalidCharacterClass = 4000,
    EmptyResponse = 9000,
    UnknownError = 9999,
}

Custom Response是父 class 只有一個UnknownErrorMessage字符串屬性,我用它來在需要時添加額外的消息,但僅限於 Unity。我的函數中不需要它。)

我的 C# Unity 項目中有相同的內容:

public class CharactersResponse : CustomResponse
{
    public CharactersCode Code;
    public string CharacterID;
}

public enum CharactersCode
{
    Success = 0,
    InvalidName = 2000,
    CharacterNameAlreadyExists = 2009,
    NoCharacterSlotAvailable = 3000,
    InvalidCharacterClass = 4000,
    EmptyResponse = 9000,
    UnknownError = 9999,
}

我仍在學習,但我發現以這種方式在 Unity 中顯示正確的消息(以及關於本地化)很有用。 當代碼為 0(成功)時,我通常需要同時獲取一些數據,如本例中的CharacterID或 CharacterLevel、 CharacterResponse等。CharacterResponse 將用於所有與字符相關的函數,如“GetAllCharacters”、“CreateNewCharacter” “ ETC..

我的 Function ( CreateNewCharacter ) 看起來像這樣:

import * as functions from "firebase-functions";
import { initializeApp } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { CharactersResponse } from "./CharactersResponse";
import { CharactersCode } from "./CharactersResponse";
import { StringUtils } from "../Utils/StringUtils";

// DATABASE INITIALIZATION
initializeApp();
const db = getFirestore();

// CREATE NEW CHARACTER
export const CreateNewCharacter =
  functions.https.onCall((data, context) =>
  {
    // Checking that the user is authenticated.
    if (!context.auth)
    {
      // Throwing an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
        'while authenticated.');
    }

    // TEST
    data.text = '';

    // Authentication / user information is automatically added to the request.
    const uid: string = context?.auth?.uid;
    const characterName: string = data.text;

    // Check if UserID is present
    if (StringUtils.isNullOrEmpty(uid))
    {
      // Throwing an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError('failed-precondition', 'Missing UserID in Auth Context.');
    }

    const response = new CharactersResponse();

    if (StringUtils.isNullOrEmpty(characterName))
    {
      response.Code = CharactersCode.InvalidName;
      console.log("character name null or empty return");
      return response; // PROBLEM IS HERE *****************
    }

    console.log("end return");
    return "Character created is named : " + characterName + ". UID = " + uid;
  });

在 Unity 中,function 調用如下所示:

private static FirebaseFunctions functions = FirebaseManager.Instance.Func;

    public static void CreateNewCharacter(string text, Action<CharactersResponse> successCallback, Action<CharactersResponse> failureCallback)
    {
        Debug.Log("Preparing Function");
        // Create the arguments to the callable function.
        var data = new Dictionary<string, object>();
        data["text"] = text;

        // Call the function and extract the operation from the result.
        HttpsCallableReference function = functions.GetHttpsCallable("CreateNewCharacter");
        function.CallAsync(data).ContinueWithOnMainThread((task) =>
        {
            if (task.IsFaulted)
            {
                foreach(var inner in task.Exception.InnerExceptions) 
                {
                    if (inner is FunctionsException)
                    {
                        var e = (FunctionsException)inner;
                        // Function error code, will be INTERNAL if the failure
                        // was not handled properly in the function call.
                        var code = e.ErrorCode;
                        var message = e.Message;

                        Debug.LogError($"Code: {code} // Message: {message}");

                        if (failureCallback != null)
                        {
                            failureCallback.Invoke(new CharactersResponse()
                            {
                                Code = CharacterCode.UnknownError,
                                UnknownErrorMessage = $"ERROR: {code} : {message?.ToString()}"
                            });
                        }
                    }
                }
            }
            else
            {
                Debug.Log("About to Deserialize response");
// PROBLEM IS HERE *********************
                CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
                Debug.Log("Deserialized response");

                if (response == null)
                {
                    Debug.LogError("Response is NULL");
                }
                else
                {
                    Debug.Log("ELSE");
                    Debug.Log($"Response: {response}");
                    Debug.Log(response.Code.ToString());
                }
            }
        });
    }

問題:在我的 Unity C# 代碼中, task.Result.Data包含我在 function 中設置的CharactersCode ,但我找不到將其轉換為 CharactersResponse 的方法。 (它在 Azure 函數中起作用)。 此外,Deserialization Debug.Log("Deserialized response");之后的行沒有被執行。 代碼似乎卡在了反序列化過程中。

我嘗試使用和不使用CustomResponse擴展我的 TypeScript class(因為我的 Function 中不需要它,所以我一開始沒有擴展它)。 我還嘗試設置一個 CharacterID,因為我認為它可能不喜歡這個屬性丟失的事實,但結果是一樣的。

我不明白這里有什么問題? 如果你們中的任何一個可以提供幫助。

謝謝。

HttpsCallableResult.Data的類型為object

=> 您的ToString將簡單地返回類型名稱,例如

System.Object

或者在您的情況下,結果是一個字典,因此它會打印出該類型。

=> 這當然不是有效的 JSON 內容,也不是您所期望的。

只需自己從數據中構建結果:

var result = (Dictionary<string, object>)task.Result.Data;
CharactersResponse response = new CharactersResponse
{
    Code = (CharactersCode)(int)result["Code"],
    CharacterID = (string)result["CharacterID"];
};       

我想實施 derHugo 的解決方案,但找不到將task.Result.Data轉換為Dictionary<string, object>的方法。 代碼卡在var result = (Dictionary<string, object>)task.Result.Data; 即使在逐步調試中也沒有彈出錯誤。

舊解決方案:

所以我做了一些研究並偶然發現了這篇文章並最終改用了這個:

var json = JsonConvert.SerializeObject(task.Result.Data);
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(json);

我基本上將task.Result.Data轉換為 JSON 並將其轉換回CharactersResponse並且它有效。 我有我想要的。 然而,我似乎明白這不是性能方面的最佳解決方案,但現在還可以,我現在可以繼續推進項目,稍后我會嘗試找到更好的解決方案。

新解決方案:

出於好奇,我想嘗試最后一件事。 我想知道如果我在開始時(在我的函數中)而不是在結束時(在我的 Unity 應用程序中)轉換為 JSON 會怎么樣。 所以我在函數的 TypeScript 代碼中這樣做了:

response.Code = CharactersCode.InvalidName;
var r = JSON.stringify(response); // Added this line
return r; // return 'r' instead of 'response'

在我的 C# 代碼中,我重試了這行代碼:

CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());

它有效。 我只需要在返回之前將我的 object 轉換為 function 中的 JSON。 與舊解決方案相比,它允許我“節省”一行代碼在客戶端進行處理。

感謝 derHugo 的回答,因為它幫助我找到了我想要的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM