Timekeeping For Digital Gods

by bytes and yonada

As creators of virtual worlds, our goal is to create environments that are engaging and interesting for users. This means finding a balance between designing a digital physics that allows for complex and unexpected behaviors to emerge, and ensuring that the available infrastructure can support these behaviors. To do this, we must consider three main dimensions of digital physics: time, the form of its laws, and the scope within which these laws apply.

Time

We refer to the passing of time in a virtual world as the iterative application of the world's laws upon itself. Each discrete application is an “instant” within the world's flow of time. One way to design in-world time is to have it progress continuously alongside external time. In a virtual world implemented on a blockchain, each block corresponds to a certain number of instants passing within the world, regardless of the transactions contained in the block. This is known as synced time, or “ticking”. This approach can make the world more interesting for users as it allows them to see the consequences of their actions unfold in real time. Additionally, it leads to more time passing within the world, with the world continuously updating, which in turn facilitates the emergence of interesting behaviors.

However, there are drawbacks to this approach. A larger expanse of time generally requires more computational resources, which can quickly surpass the capacity of a chain or server. It can also be difficult to implement this system on a regular blockchain, as all onchain changes must be initiated by transactions from external users.

This difficulty becomes clear when you imagine something that seems simple on the surface: an onchain game with NPCs. On mainnet Ethereum, you could define an update function that sets the position of every NPC on the game map and have an external account call it periodically to update their location. But this would be unreliable as you have no guarantee that the external account won’t be outbid for gas at a block where it’s meant to call the update. The structure of time in your game would drift as a result (see the original CryptoKitties giveBirth() function as an example of this; amid increasing gas prices onchain, Axiom Zen actually had to increase the reward given to callers of the giveBirth function to ensure that the transaction to birth a new NFT was called 256 blocks after a Kitty was bred by a user). We’ll refer to this approach using external accounts as manual ticking.

Custom rollups give us more flexibility to add the ticking functionality onchain, without the need for external accounts, and where the progression of synced time is guaranteed by the protocol. We’ll refer to this approach as automatic ticking. Automatic ticking can be implemented by writing a “tick contract” which is called by the protocol itself, rather than by an external account.

As an example, bytes worked on a proof-of-concept ticking chain built on the OP Stack which runs an automatically ticking implementation of Conway’s Game of Life (you can find a video presentation of this here). Bytes used a modified system transaction to automatically call the contract that ticks the cellular automata simulation. To fully test the limits of the chain itself, he implemented the game in two ways: as a Solidity smart contract running onchain, and as a precompile in the chain itself. The Solidity implementation maxed out the CPU after reaching a 70x70 grid at two updates per block (1 block/second, or ~10k cells/second), while the chain with a custom precompile engine maxed out at a 256x256 grid at the same rate using ~6% of the CPU (~130k cells/second).

The key phrase in the last paragraph’s final sentence is “maxed out.”  A ticking chain adds an additional layer of complexity: with each block, an increasing amount of state needs to be touched by the transaction that ticks the game of simulation. The rollup node is eventually bottlenecked by raw compute (CPU, disk IO etc.). The only solution here is higher-capacity nodes.

The alternative to synced time is unsynced time. Under this scheme, the passage of time in the world does not necessarily advance when external time does. Instead, time moves forward in response to certain events, usually user actions. Traditional board games that don't involve a timer would fall into a similar category. Unsynced time is easier to implement onchain, as it fits the model that blockchains were designed to support. However, it also sacrifices some of the features that could make the world more interesting (like automatically-moving NPCs).

An early version of WildWood, a proof-of-concept game from David Huang and cha0sg0d, illuminates this sacrifice. In this game, two players must defend their base from a siege of aggressive NPCs. In an early version of the game, NPC movements were only triggered when players themselves moved – an implementation of unsynced time which was not very realistic. After adding ticking, NPCs moved – but another issue still remained. The chain ticked once per second, which meant the game had to use optimistic updates to broadcast a player’s position on the map if they moved more than once per second. However, your teammate would not have an automatic view of your client, which meant there was a lag in player position updates. To overcome this, the team made use of MUD’s relay service, a peer-to-peer network for broadcasting a local client to the entire board. And voila, the transition from unsynced to synced time was achieved.

Closed and Open-Form Laws

Worldbuilders must also decide whether the mathematical laws governing their virtual world will follow open- or closed-form expressions. Closed-form expressions have a fixed number of operations. With open-form (or recursive) expressions, however, the number of operations grows depending on a given variable. Under open-form expressions, the future state of the world can only be calculated by repeatedly applying the laws of the world to a known state. Complex, live environments, such as Dwarf Fortress, usually fall into this category. Closed-form expressions, on the other hand, allow for any future state in a constant amount of time to be calculated from a past state and the time that has elapsed between them (assuming no future user actions alter the state), like the rate of resource extraction in Age of Empires II.

Open-form expressions can make a virtual world more interesting because, like the real world, they are not predictable. It takes increasing amounts of time and computational resources to predict the future state of the world (Conway's Game of Life implemented onchain is a good example of this: you can't compute an arbitrary state in the future because you need to run the game in time). Furthermore, unexpected macroscopic behaviors can emerge from simple microscopic interactions. In a world governed by closed-form expressions, these emergent behaviors generally occur only externally, through the actions of users (who themselves behave like open-form expressions), rather than within the physics of the world itself.

This trade-off between open- and closed-form expressions involves a similar balance as time. Closed-form expressions may decrease the potential interestingness of the world, but they also make it more computationally efficient. Closed-form expressions can be used with either synced or unsynced time. When implemented on a blockchain, they have a significant advantage over open-form expressions when time is synced. Because the cost of any length of time is constant, the world can be designed so that the onchain state is only updated when a user sends a transaction, but it is set to the state it would have been in after the time since the last update had elapsed.

The Scope of Time and Form

Consider the current standard for on-chain dynamism, a method known as a lazy update. In a lazy update, the player initiates the beginning and end of an action, but time in between is simulated instead of calculated directly. For example, a player plants an apple tree at block 1 and then later harvests the apples at block 10. Lazy update logic could be written such that the player can harvest one apple for each time unit, a total of 9 apples. This is fine for updating logic that has a closed-form function (like one apple per block), but if the farming logic changes based on inputs in between the player’s actions, this method breaks down. If at block 5, a rainstorm arrived and increased the apple growth rate, and at block 7, a plague of locusts nearly decimated the crop, it is no longer possible to calculate how many apples the player can harvest at block 10 in an efficient way without actually applying all of the events that have occurred (you wouldn’t have the computational power to catch up to the new state). Lazy updates are still very useful for cheap calculation of certain living things, such as plants with a fixed growth rate, but they fall short of a complete toolkit for a dynamic world.

In the real world, time passes everywhere, all at once, in a potentially infinite universe (with some relativistic intricacies). In virtual worlds, this is not necessarily the case.

First, the virtual world may be noticeably finite. The potential for interestingness tends to increase with size—more happens in a world made of two billion galaxies than in a world made of two atoms—but so does the computational cost. Both of these relationships are closely tied to the two trade-offs previously mentioned: the passage of time and the form of physics.

Second, time does not have to pass everywhere within the virtual world. The world can be divided into discrete regions where time passes differently, in order to reduce the computational burden of the world. For example, more sophisticated and expensive physics can be used in regions where there is user activity, while simpler physics can be used in areas where there is no activity. The downside of this approach is twofold: it can make the world seem inconsistent and lacking integrity, which limits the design space for the laws of the world and puts a burden on worldbuilders to avoid confusing users; and it also puts limits on how causality can travel within a world, as actions in one area cannot have consequences in a distant area if the space between them is frozen in time. The size of the regions where the physics apply is a major design consideration that will impact the resources needed for the world and the level of interestingness it can achieve.

To create an interesting and engaging virtual world, it is necessary to carefully balance computational efficiency with interestingness. This includes deciding on the type of time to use (synced or unsynced) and evaluating the form of the physics laws that will govern the world. The size of the regions wherein the physics apply is another important decision. By making these choices carefully, not only can worldbuilders achieve interestingness while keeping the computational burden of the world manageable, they can also create a highly fertile creative substrate for other developers to build on top of.

A schema for conceptualizing how games implement time and laws
A schema for conceptualizing how games implement time and laws

A version of this piece, with collaboration from cha0sg0d, originally appeared in the Autonomous Worlds book, published by 0xPARC

To learn more about Lattice and MUD, visit our website, read our documentation, or join our Discord server

Thank you to vera, ludens, biscaryn, kooshaba, and kooshaza for edits and feedback on this piece.

Subscribe to The New World
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.