> ## 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.

# Source code and best practices - Winter Starfall

> Download the Winter Starfall PlayFab demo game source code and walk through best-practice code samples for player login and in-game purchase scenarios.

# Source code and scenarios in Winter Starfall

This tutorial contains instructions for downloading and running the source code for Winter Starfall, and a walkthrough of the code samples demonstrating best practices for login and purchase scenarios.

## Source code download

### Prerequisites

* Install Node JS
* Install Visual Studio Code

### Setup for local development

1. Download the source code from [GitHub](https://github.com/PlayFab/winter-starfall).
2. In VS Code, open the `/website` folder and opt to install all the extensions it recommends.
3. In the `/website` directory, run npm install to install all dependencies.
4. Use `npm run dev` to start the site. Select the link it offers to view the site.

### Azure credentials

To update the AZURE\_CREDENTIALS secrets, run this command in the Azure Portal shell:

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

## Login flow

The player authentication flow in Winter Starfall has two steps: the login call and post-login functions that are used to get the player's information from the service and load them into the correct location in the story.

### Player authentication

For the login portion, the file [use-login.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-login.ts) defines a TypeScript wrapper function that handles the multiple authentication methods available to players.

Lines 144-169 define the callback function for logging in with an email address, and call the post-login functions if login succeeds.

```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]);
```

The game offers 3 recoverable methods for player authentication: email, Google, and Facebook, so that player accounts will never be lost. For more information, see [Login best practices](/services/playfab/identity/player-identity/login/login-basics-best-practices).

### Post login: Getting player data

Once the security token has been retrieved, it is used to run the post login functions to get the saved game state.

Lines 340-378 define **postLoginFunctions**, which calls various Economy and PlayFab Services APIs to load the correct story location, inventory items, currency, etc based on the player.

```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);
        });
});
```

* First `ClientGetTitleData` gets the secret key needed for all API calls
* Then `EconomySearchItems` searches the catalog for currency and item types
* Which is used as input to `EconomyGetInventoryItems` to return the player's inventory items
* Then `ClientGetUserData` retrieves the value that stores the story location as part of a player's data.

## Purchase flow

At certain points in the game, the player has the option to purchase and sell inventory items from the store. The purchase flow is implemented in another wrapper function. In the file [use-store.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-store.ts), lines 40-69 bring up the correct store that the player encounters at a specific location and populates the items for sale by searching the catalog.

```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` is defined in [use-playfab.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-playfab.ts) at lines 423-446, within which the PlayFab Economy API `GetItems` is used to search the catalog and return items.

```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]
    );
```

When a purchase is conducted, a call to the `PurchaseInventoryItems` API is made. This is defined in [use-playfab.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-playfab.ts) at lines 540-565.

### Selling items

The selling flow is an example of how to use Azure Functions driven CloudScript to extend the functionality of the Economy system.

[SellItem.cs](https://github.com/PlayFab/winter-starfall/blob/main/azure-functions/SellItem.cs) defines the CloudScript function that executes when selling an item.

Lines 49-61 are where PlayFab's `GetTitleData` API is used to return the store multiplier and calculate the sale price.

```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;
```

The CloudScript function then gets called in lines 161-187 of [use-store.ts](https://github.com/PlayFab/winter-starfall/blob/main/website/src/hooks/use-store.ts).

```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>
  To run Winter Starfall locally you'll need an Azure account to support the CloudScript functions. You can [sign up for a free Azure account](https://azure.microsoft.com/pricing/purchase-options/azure-account?msockid=1dec68fa155462cb2baa7ca6147963d7) and then follow the instructions above to reset the Azure credentials correctly.
</Note>

## See also

* Login flow
  * [Player login documentation](/services/playfab/identity/player-identity/login)
* Purchase flow
  * [Economy V2 documentation](/services/playfab/economy-monetization/economy-v2/overview)
  * Another good next step to learning more about Economy V2 is to try out the [crafting game tutorial](/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/game-context), which focuses on building a sample game using the store and inventory functions.


## Related topics

- [Winter Starfall PlayFab demo game overview](/services/playfab/demo-game/overview.md)
- [Vanguard Outrider (legacy)](/services/playfab/demo-game/legacy-vanguard-outrider.md)
- [Best practices](/services/xbox-services/develop/best-practices/index.md)
- [Insights Best Practices](/services/playfab/data-analytics/legacy/insights/best-practices.md)
- [Best practices for calling XBOX services](/services/xbox-services/develop/best-practices/live-best-practices-calling-xbl.md)
