Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONVERSION_MANUAL.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ReadMe.io to MkDocs Conversion Manual
# ReadMe.io to MkDocs Conversion Manual

This manual documents the step-by-step process for converting ReadMe.io documentation to MkDocs-compatible markdown format.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- [Inventory](inventory-overview.md)
- [Federated Inventory](inventory-federated.md)
- [Virtual Currency](virtual-currency-overview.md)
- [Stores](stores-overview.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Inventory federated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phrasing/branding question: do we want to call this "Federated inventory" or "Inventory federation"? (in the latter case, I think "federation" reads slightly better than "federated")


## Overview

Beamable supports custom inventory federation using managed [microservices](../../cloud-services/microservices/microservice-framework.md). You can use this service to extend the Inventory system with items that are managed externally.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth mentioning NFTs here? I see that you have them in the bullet list below, so maybe not needed in the prose intro, but it might clarify "why federate inventory?" to lead with it as early as possible.


Some use cases:

- Crypto assets - use NFTs as inventory items and auto-mint new items
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there public connotations on the word "Crypto" that we could dodge by saying "Blockchain" instead? I am not super tapped into the scene, but I feel like I have heard "Crypto" spoken in the tones of a not-so-nice epithet. Probably not a big deal, either way.

- Generative AI - add support for granting players AI-generated items

**Requirements:**

- Federated Inventory depends on the implementation of [Federated Identity](../identity/federated-identity.md), that means that every player first
needs to have a federated identity with the same microservice that federates inventory
- Inventory items are content-driven. To enable federation, content items must be marked as federated to a specific microservice

The diagrams below illustrate the flows for retrieving and granting the Inventory Items with Federation enabled

![Federated Inventory Get Flow](../../../../media/imgs/federated-inventory-get-flow.png){: style="height:auto;width:400px"}


![Federated Inventory Grant Flow](../../../../media/imgs/federated-inventory-grant-flow.png){: style="height:auto;width:400px"}

### `IFederatedInventory<T>` interface

To use the Federated Inventory you should start by implementing the `IFederatedInventory<T>` interface in your microservice.
Note that this interface also implements `IFederatedLogin<T>` because federated authentication is a prerequisite.
`T` must be your implementation of the `IThirdPartyCloudIdentity` - a basic interface that
requires you to define a unique name/namespace for your federation. This enables you to have multiple federation
implementations in a single microservice.

```csharp
public class MyFederationIdentity : IThirdPartyCloudIdentity
{
public string UniqueName => "my-cool-federation";
}

[Microservice("MyFederation")]
public class MyFederationService : Microservice, IFederatedInventory<MyFederationIdentity>
{
public Promise<FederatedAuthenticationResponse> Authenticate(string token, string challenge, string solution)
{
throw new System.NotImplementedException();
}

public Promise<FederatedInventoryProxyState> GetInventoryState(string id)
{
throw new System.NotImplementedException();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the implementation of inventory-related stuff is the meat and potatoes of this article, so having a skeletonized version here is a bit of a distraction. Could the sections below stand on their own without this block of code?

}

public Promise<FederatedInventoryProxyState> StartInventoryTransaction(string id, string transaction, Dictionary<string, long> currencies, List<FederatedItemCreateRequest> newItems, List<FederatedItemDeleteRequest> deleteItems, List<FederatedItemUpdateRequest> updateItems)
{
throw new System.NotImplementedException();
}
}
```

### `GetInventoryState` implementation

You can do any custom logic here. For example, you could AI generate some items, load items from a smart contract, use microstorage, or do anything that satisfies your specific requirements.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For tech writing, I am a big fan of "perform" in place of "do" -- makes it sound techy without being too professorial ;)

Here's a dummy example that will return some static items and currency, just to showcase the response structure:

```csharp
public Promise<FederatedInventoryProxyState> GetInventoryState(string id)
{
return Promise<FederatedInventoryProxyState>.Successful(
new FederatedInventoryProxyState
{
currencies = new Dictionary<string, long>
{
{ "currency.federated-gold", 1000 },
{ "currency.federated-silver", 5000 }
},
items = new Dictionary<string, List<FederatedItemProxy>>
{
{
"items.avatar", new List<FederatedItemProxy>
{
new()
{
proxyId = "externalAvatarId1",
properties = new List<ItemProperty>
{
new()
{
name = "level",
value = "20"
},
new()
{
name = "color",
value = "blue"
}
}
},
new()
{
proxyId = "externalAvatarId2",
properties = new List<ItemProperty>
{
new()
{
name = "level",
value = "30"
},
new()
{
name = "color",
value = "red"
}
}
Comment on lines +80 to +112
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to make this take less vertical space? My eyes kinda glaze over when I see long stretches of sparse vertical code 😇

}
}
}
}
}
);
}
```

The important thing to emphasize here is the `id` argument. It's the same external account id that you return from the `Authenticate` method. If you want to access the player's id in the Beamable system, you can use `this.Context.UserId`
As an example, you can use the wallet address as an external user identifier when implementing blockchain federation.

### `StartInventoryTransaction` implementation

The Inventory service will forward all the changes against federated currency and items. This method has the same return type as the previous one.

```csharp
public Promise<FederatedInventoryProxyState> StartInventoryTransaction(string id, string transaction, Dictionary<string, long> currencies, List<FederatedItemCreateRequest> newItems, List<FederatedItemDeleteRequest> deleteItems, List<FederatedItemUpdateRequest> updateItems)
{
await _myFederation.ApplyCurrency(currencies);
await _myFederation.AddItems(newItems);
await _myFederation.UpdateItems(updateItems);
await _myFederation.DeleteItems(deleteItems);
return await GetInventoryState(id);
}
```

The `transaction` argument is a unique transaction id generated in our Inventory service, and you can use it to guard against multiple submissions.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever possible, using "the Beamable [thing]" instead of "our [thing]" lends it a nice tech-writing air.


If your transaction processing is too slow to return a timely response, you can implement the async approach.
Process the transaction in the background and return the current state. Once the transaction finishes processing,
you can report back the new state like this:
Comment on lines +142 to +144
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What techniques should the game developer use for this? Simply omit the await keyword when invoking an async function? Use the Job Scheduler? Something else?


```csharp
await Requester.Request<CommonResponse>(Method.PUT, $"/object/inventory/{_userContext.UserId}/proxy/state", newState);
```

The Inventory service will notify the game client to refresh the inventory content if there's a diff.
Loading