> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 原始程式碼和最佳做法 - Winter Starfall

> 下載 Winter Starfall PlayFab 示範遊戲原始程式碼，並逐步了解玩家登入和遊戲內購買案例的最佳做法程式碼範例。

# Winter Starfall 中的原始程式碼和案例

本教學課程包含下載和執行 Winter Starfall 原始程式碼的指示，以及示範登入和購買案例最佳做法的程式碼範例逐步解說。

## 原始程式碼下載

### 先決條件

* 安裝 Node JS
* 安裝 Visual Studio Code

### 本機開發設定

1. 從 [GitHub](https://github.com/PlayFab/winter-starfall) 下載原始程式碼。
2. 在 VS Code 中，開啟 `/website` 資料夾，並選擇安裝其建議的所有延伸模組。
3. 在 `/website` 目錄中，執行 npm install 以安裝所有相依性。
4. 使用 `npm run dev` 啟動網站。選取其提供的連結以檢視網站。

### Azure 認證

若要更新 AZURE\_CREDENTIALS 祕密，請在 Azure 入口網站殼層中執行此命令：

`az ad sp create-for-rbac --name "VanguardOutrider2" --role contributor --scopes /subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME --json-auth`

## 登入流程

Winter Starfall 中的玩家驗證流程有兩個步驟：登入呼叫，以及用來從服務取得玩家資訊並將其載入故事中正確位置的登入後函式。

### 玩家驗證

在登入部分，檔案 [use-login.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-login.ts) 定義了一個 TypeScript 包裝函式，用來處理玩家可用的多種驗證方法。

第 144-169 行定義了使用電子郵件地址登入的回呼函式，並在登入成功時呼叫登入後函式。

```typescript theme={null}
const onLogin = useCallback(() => {
    setIsLoading(true);
    setLoginMethodInProgress("email");

    dispatch(siteSlice.actions.loginSteps(loginEventCount));

    ClientLoginWithEmailAddress({ Email: data.email, Password: data.password })
        .then(result => {
            dispatch(siteSlice.actions.login(result));
            dispatch(siteSlice.actions.loginStepsAdvance());
            // Track logins from returning users
            trackEvent({ name: "Returning User", properties: {} });
        })
        .then(() => {
            return postLoginFunctions();
        })
        .then(() => {
            setIsLoading(false);
            navigate(routes.Explore());
        })
        .catch(problem => {
            dispatch(siteSlice.actions.loginStepsReset());
            onError(problem);
        })
        .finally(() => dispatch(siteSlice.actions.loginStepsReset()));
}, [ClientLoginWithEmailAddress, data.email, data.password, dispatch, navigate, onError, postLoginFunctions]);
```

這款遊戲提供 3 種可復原的玩家驗證方法：電子郵件、Google 和 Facebook，因此玩家帳戶永遠不會遺失。如需詳細資訊，請參閱[登入最佳做法](/zh-TW/services/playfab/identity/player-identity/login/login-basics-best-practices)。

### 登入後：取得玩家資料

擷取安全性權杖之後，就會使用它來執行登入後函式，以取得已儲存的遊戲狀態。

第 340-378 行定義了 **postLoginFunctions**，它會呼叫各種 Economy 和 PlayFab Services API，以根據玩家載入正確的故事位置、庫存物品、貨幣等。

```typescript theme={null}
return new Promise<void>((resolve, reject) => {
    ClientGetTitleData({ Keys: TITLE_DATA_KEYS_ALL })
        .then(result => {
            dispatch(siteSlice.actions.titleData(result));
            dispatch(siteSlice.actions.loginStepsAdvance());
            loadScripts();
        })
        .then(() =>
            EconomySearchItems({
                Count: SEARCH_ITEMS_MAX_COUNT,
                Filter: "type eq 'currency' or type eq 'catalogItem'",
            })
        )
        .then(result => {
            dispatch(siteSlice.actions.catalog(result.Items));
            dispatch(siteSlice.actions.loginStepsAdvance());
        })
        .then(() => EconomyGetInventoryItems({ Count: SEARCH_ITEMS_MAX_COUNT }))
        .then(result => {
            dispatch(siteSlice.actions.inventory(result.Items));
            dispatch(siteSlice.actions.loginStepsAdvance());
        })
        .then(() => ClientGetUserData({ Keys: USER_DATA_KEYS_PLAYER_ALL }))
        .then(result => {
            dispatch(siteSlice.actions.userDataPlayer(result));
            dispatch(siteSlice.actions.loginStepsAdvance());
        })
        .then(() => ClientGetUserReadOnlyData({ Keys: USER_DATA_KEYS_READONLY_ALL }))
        .then(result => {
            dispatch(siteSlice.actions.userDataReadOnly(result));
            dispatch(siteSlice.actions.loginStepsAdvance());
        })
        .then(() => {
            resolve();
        })
        .catch(problem => {
            dispatch(siteSlice.actions.loginStepsReset());
            reject(problem);
        });
});
```

* 首先，`ClientGetTitleData` 會取得所有 API 呼叫所需的祕密金鑰
* 接著，`EconomySearchItems` 會在目錄中搜尋貨幣和物品類型
* 這會用作 `EconomyGetInventoryItems` 的輸入，以傳回玩家的庫存物品
* 然後，`ClientGetUserData` 會擷取將故事位置儲存為玩家資料一部分的值。

## 購買流程

在遊戲中的特定時間點，玩家可以選擇從商店購買和出售庫存物品。購買流程是在另一個包裝函式中實作。在檔案 [use-store.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-store.ts) 中，第 40-69 行會開啟玩家在特定位置遇到的正確商店，並透過搜尋目錄來填入待售物品。

```typescript theme={null}
export function useEconomyStoreSingle(storeName: string): IEconomyStoreSingleResults {
    const dispatch = useDispatch();
    const store = useSelector((state: AppState) => state.site.stores).find(store =>
        store.AlternateIds?.find(friendlyId => friendlyId.Type === FRIENDLYID && friendlyId.Value === storeName)
    );
    const { isLoading, error, setError, EconomyGetItem } = usePlayFab();

    useEffect(() => {
        if (!is.null(store) || isStoreLoading) {
            return;
        }

        isStoreLoading = true;

        EconomyGetItem({
            AlternateId: { Type: FRIENDLYID, Value: storeName },
        })
            .then(results => {
                dispatch(siteSlice.actions.storeAdd(results.Item as PlayFabEconomyModels.CatalogItem));
            })
            .catch(setError)
            .finally(() => {
                isStoreLoading = false;
            });
    }, [EconomyGetItem, dispatch, setError, store, storeName]);

    return {
        error,
        isLoading,
        store,
    };
}
```

`EconomyGetItem` 定義於 [use-playfab.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-playfab.ts) 的第 423-446 行，其中使用 PlayFab Economy API `GetItems` 來搜尋目錄並傳回物品。

```typescript theme={null}
const EconomyGetItems = useCallback(
        (request: PlayFabEconomyModels.GetItemsRequest): Promise<PlayFabEconomyModels.GetItemsResponse> => {
            const date = startRequest("EconomyApi", "GetItems", request);

            return new Promise((resolve, reject) => {
                PlayFab.EconomyApi.GetItems(request, (result, problem) => {
                    endRequest(date, problem, result);

                    if (!is.null(problem)) {
                        return reject(problem);
                    }

                    if (result.code !== 200) {
                        return reject(formatPlayFabNon200Error(result));
                    }

                    return resolve(result.data);
                }).catch(reason => {
                    catchRequest(reject, reason);
                });
            });
        },
        [catchRequest, endRequest, startRequest]
    );
```

進行購買時，會呼叫 `PurchaseInventoryItems` API。這定義於 [use-playfab.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-playfab.ts) 的第 540-565 行。

### 出售物品

出售流程是如何使用 Azure Functions 驅動的 CloudScript 來擴充 Economy 系統功能的範例。

[SellItem.cs](https://github.com/PlayFab/winter-starfall/blob/main/azure-functions/SellItem.cs) 定義了出售物品時執行的 CloudScript 函式。

第 49-61 行使用 PlayFab 的 `GetTitleData` API 傳回商店乘數並計算售價。

```csharp theme={null}
 // Get your sell multiplier
var titleData = await PlayFabFunctions.GetTitleDataAsync(player, new List<string> { TitleDataKeys.Multipliers }, log);
var sellMultiplier = JsonConvert.DeserializeObject<Multipliers>(titleData[TitleDataKeys.Multipliers])?.sell ?? 1;
```

接著，CloudScript 函式會在 [use-store.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-store.ts) 的第 161-187 行中被呼叫。

```typescript theme={null}
export function useEconomyStoreSell(): IEconomyStoreSellItemResults {
    const { isLoading, error, setError, CloudScriptExecuteFunction, EconomyGetInventoryItems } = usePlayFab();
    const dispatch = useDispatch();

    const onSell = useCallback(
        (itemId: string, amount: number) => {
            return new Promise<void>((resolve, reject) => {
                CloudScriptExecuteFunction({
                    FunctionName: "SellItem",
                    FunctionParameter: {
                        ItemId: itemId,
                        Amount: amount,
                    },
                })
                    .then(() => EconomyGetInventoryItems({ Count: SEARCH_ITEMS_MAX_COUNT }))
                    .then(data => {
                        dispatch(siteSlice.actions.inventory(data.Items));
                        resolve();
                    })
                    .catch(issue => {
                        setError(issue);
                        reject(issue);
                    });
            });
        },
        [CloudScriptExecuteFunction, EconomyGetInventoryItems, dispatch, setError]
    );
```

<Note>
  若要在本機執行 Winter Starfall，您需要 Azure 帳戶來支援 CloudScript 函式。您可以[註冊免費的 Azure 帳戶](https://azure.microsoft.com/pricing/purchase-options/azure-account?msockid=1dec68fa155462cb2baa7ca6147963d7)，然後依照上述指示正確重設 Azure 認證。
</Note>

## 另請參閱

* 登入流程
  * [玩家登入文件](/zh-TW/services/playfab/identity/player-identity/login)
* 購買流程
  * [Economy V2 文件](/zh-TW/services/playfab/economy-monetization/economy-v2/overview)
  * 深入了解 Economy V2 的另一個良好後續步驟，是試用[製作遊戲教學課程](/zh-TW/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/game-context)，該教學課程著重於使用商店和庫存函式建置範例遊戲。


## Related topics

- [Winter Starfall PlayFab 示範遊戲概觀](/zh-TW/services/playfab/demo-game/overview.md)
- [SDK 錯誤處理最佳做法](/zh-TW/services/playfab/live-service-management/service-gateway/automation/cloudscript/sdk-error-handling-best-practices.md)
- [登入基本概念和最佳做法](/zh-TW/services/playfab/identity/player-identity/login/login-basics-best-practices.md)
- [Vanguard Outrider (舊版)](/zh-TW/services/playfab/demo-game/legacy-vanguard-outrider.md)
- [PlayFab 用量最佳做法](/zh-TW/services/playfab/pricing/consumption-best-practices.md)
