Skip to main content

Enjin Farmer: Implementation Breakdown

The Enjin Farmer sample project demonstrates a basic Enjin Platform integration within a Unity game. It's built with a client-server architecture to ensure security and scalability.

The project consists of two main components:

  • 🎮 Unity Game (Client): The front-end game that players interact with. It handles gameplay, visuals, and user input, communicating with the game server to perform blockchain actions.
  • 🖥️ Game Server (Backend): A Node.js application that manages all Enjin Platform logic. It securely handles wallet creation, token minting, and other on-chain operations on behalf of the players.

💡 Important Considerations

Before you begin, please keep the following in mind:

  • Demonstration Purpose: This is a simplified example designed to showcase a basic integration. It is not suitable for a production environment as is.
  • WebSockets: This implementation does not use WebSocket events. In a real-world application, WebSockets can simplify the process of listening for transaction finalization and receiving real-time updates, such as when a user receives an NFT from an external source like the marketplace.
  • Wallet Funding: New managed wallets are created without any funds. To cover network fees for actions like melting or transferring tokens, you must either fund each wallet individually or use a Fuel Tank to subsidize transactions for all your users.

🖥️ Game Server

The game server is a RESTful API built with Node.js and Express. It serves as the secure bridge between the game client and the Enjin Platform. The main entry point is the src/index.js file.

Environment Variables

The server is configured using the following environment variables:

VariableDescription
PORTThe port the server listens on. Defaults to 3000.
JWT_SECRETA secure, random string used for signing player authentication tokens.
ENJIN_API_URLThe Enjin Platform API URL. Use https://platform.canary.enjin.io/graphql for testing (Canary Network) or https://platform.enjin.io/graphql for production.
ENJIN_API_KEYYour API Key Token obtained from the Enjin Platform.
DAEMON_WALLET_ADDRESSThe address of your Wallet Daemon. This wallet receives the initial supply of all created tokens.
ENJIN_COLLECTION_IDThe ID of the Enjin Farmer collection. If left blank, the server will create a new collection on startup.

Server Initialization & Collection Setup

On startup, the server performs a one-time setup to ensure the necessary blockchain assets exist.

  1. Check for Collection: The server first checks if an ENJIN_COLLECTION_ID has been provided.
  2. Create Collection: If the ID is missing, the server calls the createCollection function, which executes the CreateCollection mutation on the Enjin Platform. The Wallet Daemon automatically signs the request.
    mutation CreateCollection($name: String!, ...) {
    CreateCollection(...) {
    id
    method
    state
    }
    }
  3. Monitor Transaction: The mutation returns a request ID. The server then polls the GetTransaction query until the transaction state is FINALIZED and the result is EXTRINSIC_SUCCESS.
    query GetTransaction($requestId: Int!) {
    GetTransaction(id: $requestId) {
    state
    result
    events { ... }
    }
    }
  4. Extract Collection ID: Once finalized, the server extracts the new collection ID from the transaction's events and assigns it to the ENJIN_COLLECTION_ID variable.
  5. Create NFTs: Using a similar process, the server then creates the three resource NFTs (Gold Coin, Gold Coin (Blue), and Green Gem) within the collection by calling the CreateToken mutation for each and waiting for finalization.
    mutation CreateToken($collectionId: BigInt!, $name: String!, ...) {
    CreateToken(collectionId: $collectionId, params: { ... }) {
    id
    method
    state
    }
    }

After this setup is complete, the server starts listening for API requests.

API Endpoints

The server exposes several endpoints to handle game actions. The wallet and token endpoints are protected by a JWT authentication middleware, which verifies the player's identity before processing the request.

Authentication

Wallet Management

Token Actions

  • POST /api/token/mint: Mints a token to the player's wallet. The server calls the MintToken mutation. The recipient address is extracted from the player's authenticated session.
    mutation mintToken($recipient: String!, $collectionId: BigInt!, ...) {
    MintToken(recipient: $recipient, collectionId: $collectionId, ...) {
    id
    }
    }
  • POST /api/token/melt: Melts a token from the player's wallet. This uses the Burn mutation. Since the token needs to be melted from the managed wallet's account, it needs to be signed by the managed wallet. For that, the player's managed wallet address is specified as the signingAccount. (Learn more about managed wallets here)
    mutation burnToken($signingAccount: String!, $collectionId: BigInt!, ...) {
    Burn(signingAccount: $signingAccount, collectionId: $collectionId, ...) {
    id
    }
    }
  • POST /api/token/transfer: Transfers a token from the player's managed wallet to another address. This uses the SimpleTransferToken mutation, again specifying the player's managed wallet as the signingAccount.
    mutation transferToken($signingAccount: String!, $recipient: String!, ...) {
    SimpleTransferToken(signingAccount: $signingAccount, recipient: $recipient, ...) {
    id
    }
    }

🎮 Unity Game

The Unity game is the client-facing part of the project. It focuses on gameplay and user experience while offloading all sensitive blockchain operations to the game server.

Core Components

The Enjin integration is managed by a few key scripts and a central prefab:

  • EnjinManager.prefab: The heart of the integration. This prefab is added to the Farm_Outdoor scene and configures the Host URL (e.g., http://localhost:3000) in the Inspector to connect to your game server.
  • EnjinManager.cs: A singleton controller that manages the player's session (auth token, wallet data) and exposes high-level methods like MintToken() for other game scripts to use.
  • EnjinApiService.cs: Handles all REST API communication with the game server using Unity's UnityWebRequest.
  • EnjinItem.cs: A ScriptableObject that represents the data of a blockchain item, such as its display name and its corresponding on-chain token ID.
  • UI Scripts (BackpackUI.cs, BackpackItemController.cs): Scripts that manage the UI for viewing and interacting with the player's NFT inventory.

Initial Setup & Player Authentication

  1. Health Check: On launch, the client calls the /api/auth/health-check endpoint to ensure the server is available.
  2. Login/Register: From the login screen, the player clicks "Login", which calls the EnjinManager.Instance.RegisterAndLogin() method.
  3. API Request: This triggers EnjinApiService to send a POST request to the /api/auth/register endpoint.
  4. Store Auth Token: The server responds with a JWT authentication token. The client saves this token locally using PlayerPrefs and loads it on subsequent launches for a seamless experience.

In-Game NFT Interactions

All blockchain actions are initiated by the client but securely executed by the server.

Harvesting and Minting Tokens

When a player harvests a crop with the Hoe tool, they have a chance to find a resource token.

  1. An EnjinToken GameObject appears on the harvested tile.
  2. When the player collects this GameObject, its InteractedWith() method is triggered.
  3. This calls EnjinItem.Collect(), which in turn calls EnjinManager.Instance.MintToken().
  4. EnjinManager then uses EnjinApiService to send a request to the /api/token/mint endpoint.

Viewing the Wallet (Backpack UI)

  1. Clicking the backpack icon opens the inventory screen, managed by BackpackUI.cs.
  2. The UI calls EnjinManager.Instance.GetManagedWalletTokens(), which sends a request to the /api/wallet/get-tokens endpoint.
  3. The server saves the list of tokens, and the BackpackUI populates the view with the data.
  4. The BackpackUI also subscribes to the EnjinManager.Instance.OnWalletUpdated event to automatically refresh the inventory after a token is minted, melted, or transferred.

Melting and Transferring Tokens