Aspect Use Case: On-Chain NPCs for Autonomous World Games

Jan 30, 2024

Technical

Through Aspect programming, we've added fully on-chain NPCs to the on-chain games! In this gaming world, players can register their accounts as automated NPC players who participate in battles with other real players based on algorithms!

The image below shows a game where the player controls character 209, while player 24 is a fully on-chain NPC, battling alongside real players in the Royale world.

In this article, we'll provide an overview of how Aspects (modules) on Artela can create unique possibilities on-chain games that have never been possible before!

Features for fully on-chain games

After working directly with on-chain game developers on the potential features made possible through Aspect programming, let’s start by highlighting two of the most attention grabbing features: just-in-time call and high-performance wasm computation.

Just-in-time calls

JIT (Just-in-time) Calla refer to the ability of Aspects to dynamically initiate calls to smart contracts inside a virtual machine such as EVM during the on-chain transaction execution process, allowing for the insertion of immediately-active processes during the execution of on-chain transactions.

JIT Calls enable automated execution on-chain for smart contracts, allowing contracts to dynamically decide whether to execute additional processes in real-time.

As shown in the figure below, the transaction calls a smart contract for execution, the Aspect is triggered to execute at “join points” both before and after that execution. The Aspect then dynamically signs a transaction after the contract's call execution; this transaction will then be executed immediately before the next call. Since there are no private keys to sign transactions on-chain, JIT Call initiates the transaction through the underlying AA Wallet module.

In on-chain games, this features allows game contracts to be entirely automated, automatically executing system tasks!

In a hypothetical game environment, the game world's map, weather, monsters, buffs, etc., can automatically refresh every X number of blocks. For instance, when a player kills a boss, the surrounding minions will automatically disappear, and treasure chests will open - NPCs will automatically move with the player's events!

Uniquely to Aspects on Artela, the implementation of the above scenario doesn't require modifying the game contract - the Aspect can dynamically determine the player's operations and other entities' changes before and after the execution of the game contract, and then initiate JIT Calls to control these automated entities. Aspects can even combine with AI agents, allowing characters to continue to grow automatically when the player is offline.

High-performance WASM virtual machine

If an on-chain game is designed around a persistent world, the biggest adjustment is the amount of computation required on the chain. For example, if there are a thousand players in the game world, and each player automatically updates growth data in each block, the virtual machine (EVM) needs to execute the player growth data calculation 1000 times, which creates an impractical burden.

On Artela, Aspects run in the WASM virtual machine, while smart contracts run in the virtual machine on-chain (EVM). The execution efficiency of the WASM virtual machine is at least 1-2 orders of magnitude higher than that of the EVM. If designed properly to fully utilize the Aspect WASM runtime features, Aspects on Artela can perform batch calculations and updates without disrupting gameplay or transaction speed, allowing the persistent world on-chain to periodically update automatically!

Real Case: support NPC for Royale

With these ideas in mind, let’s take it to a real life example!

This idea came first came to me when I wanted to play Royale, but due to living in a time-zone with fewer players, I had to wait in the lobby for over an hour. Royale's current implementation requires players to create rooms and wait for those rooms to be populated by other players before a game can begin.

So… we collaborated with BladeDAO to integrate Aspect into Royale, an online PvP game, and utilized JIT Calls so that automated NPCs can interact directly with players.

So the idea was to add a batch of NPC characters to Royale, even if no players enter the room, the game will begin immediately when I create the room - and players can join dynamically, replacing the NPCs. In the example below, the NPC and I are both Royale player characters, both controlled by wallets, with the same character attributes - the only difference is that one of us is managed by Aspect, while I'm controlled by my own EoA. Therefore, I can battle anytime, anywhere without waiting for players to join my room - all while remaining fully on-chain.

Aspect Design

Let’s take a look at how the above use-case Aspect was designed. This is a very simple example, and can be summed up by two main processes:

  • Register player

    On Aspect, register the wallet as a player controlled by the Aspect, and the Aspect will automatically issue JIT Calls for these wallets based on algorithms.

  • Join point at pre-contract call

    Every transaction calling the Royale contract will trigger the Aspect before the contract executes. If this transaction calls the player's move method, it will then use the registered player to initiate a move call, controlling the NPC movement.


<tx:call move>
  |
  |                        _ _ _ _ _ _ _ _ _ _ _
  |                       |                     |
  |--pre_contract_call--> |     JIT Aspect      | <-------  <tx:register player>
  |                       |                     |
  |                        _ _ _ _ _ _ _ _ _ _ _
  |                           |
  v                           |
-----------------             | <tx:call move>
|               |             |
|  Royale       |<

Implements

The core method of this Aspect is as follows: this code shows the execution logic of the join point.

The Aspect will judge the call data of this contract call, parsing out the current call's method. If the method's value move(uint8) has a hash value, then it will go to get sysPlayers, and initiate a move call this.doMove(sysPlayers[i], input);.

tsxCopy code
preContractCall(input: PreContractCallInput): void {

        let calldata = uint8ArrayToHex(input.call!.data);
        let method = this.parseCallMethod(calldata);

        // if method is 'move(uint8)'
        if (method == "0x70e87aaf") {
            let currentCaller = uint8ArrayToHex(input.call!.from);
            let sysPlayers = this.getSysPlayersList();
            let isSysPlayer = sysPlayers.includes(this.rmPrefix(currentCaller).toLowerCase());

            // if player moves, sys players also move just-in-time
            if (!isSysPlayer) {
                // do jit-call
                for (let i = 0; i < sysPlayers.length; ++i) {
                    this.doMove(sysPlayers[i], input);
                }
            } else {
                // if sys player moves, do nothing in Aspect and pass the join point
                return;
            }
        }
    }

In the doMove method, the Aspect will initiate a JIT Call by first randomly selecting a movement direction, and then constructing the call data.

Using JitCallBuilder to build a JIT Call request, the JIT Call is initiated through sys.hostApi.evmCall.jitCall(request). This is a synchronous request; it will return after this call is over, after the character moves in the Royale game contract.

tsxCopy code
doMove(sysPlayer: string, input: PreContractCallInput): void {
        // init jit call
        let direction = this.getRandomDirection(input);

        let moveCalldata = ethereum.abiEncode('move', [
            ethereum.Number.fromU8(direction, 8)
        ]);

        // Construct a JIT request (similar to the user operation defined in EIP-4337)
        let request = JitCallBuilder.simple(hexToUint8Array(sysPlayer),
            input.call!.to,
            hexToUint8Array(moveCalldata)
        ).build();

        // Submit the JIT call
        let response = sys.hostApi.evmCall.jitCall(request);

        // Verify successful submission of the call
        sys.require(response.success, `Failed to submit the JIT call: ${sysPlayer}, err: ${response.errorMsg}, ret: ${uint8ArrayToString(response.ret)}`);
    }

More than NPCs

As simple as this is, Aspect described above is the full process - from idea, to design, to implementation of a fully on-chain game that supports on-chain NPCs. Something that has NEVER been possible before.

Aspects can not only bring automation and high-performance computing to on-chain gaming, but also composability to the AW! In the above example of Royale, the process of adding NPCs didn't modify the Royale game contract, which means that NPC’s and player character are effectively the same type of player, from the game’s “point of view.”

In other words, persistent world games can also be dynamically managed and updated through community governance, to perfect the world's gameplay and rules, making possible a truly composable world!

Check out this example for yourself at: JIT Royale.

If YOU’RE interested in building the next generation of on-chain games with Artela (which, if you’re still reading, you totally are), join our Developer Portal, Discord, X, and send us an email at info@artela.network. We can’t wait to work with you!

Build

Explore