LoginSignup
20
5

More than 1 year has passed since last update.

Symbol Mosaic Restrictions in Game

Last updated at Posted at 2021-12-23

Introduction

The mosaic restriction is one of the coolest features in Symbol.

Mosaic restrictions allow mosaic creators to decide which accounts can transact with the asset.

To use mosaic restrictions, there are two types of transactions needed.

I wanted to write a use case about it, but I didn't have the idea until recently, when I saw some community members share in Twitter that they were building a game on Symbol. So I wondered, it may be a good idea to use the mosaic restriction feature to create a simple concept of the Dungeons & Dragons game.

Game Rules

  • Players have a level, and skill stat.
  • Players are rewarded 1 skill point if they win a round.
  • There are some skills available such as Punch, Kick, Fire Spell
  • Skill points can upgrade a player's skill stat, to unlock new skills.

How it works

In the D&D games, we always have players and Dungeon masters.

Create Mosaics

We use Mosaics to represent game assets, such as level, punch, kick, and fire_spell.

Note: Mosaic Restrictions can only be used if isRestrictable is set to true on a mosaic.

const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

const isSupplyMutable = true; // Easily adjust the supply
const isTransferable = false; // Players cannot transfer skills to other players
const isRestrictable = true; // Enable mosaic restrictions
const isRevokable = true;  // Creator can collect back mosaics

const nonce = MosaicNonce.createRandom();
const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create(
  Deadline.create(epochAdjustment),
  nonce,
  MosaicId.createFromNonce(nonce, dungeon_master.address),
  MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable, isRevokable),
  0, // divisibility
  UInt64.fromUint(0), // unlimited
  networkType,
);

Create Mosaic Global Restriction Transaction

Up to this stage, we can set up mosaic global restrictions for each mosaic (asset).

In global restrictions, you are allowed to add reference mosaic to restrictions, which I think is more advanced.

But do not worry, I will break it down in a table so you have a better view:

MosaicId Key Value Type Reference Mosaic Id Reference Mosaic Key Reference Value Reference Type
6AACF58D95F016C0 level 0 GT null null null null
73E60A6DD1D16FDD punch 1 EQ null null null null
0015F52218DA969C kick 2 EQ null null null null
6A53A4FB36203CCE fire_spell 2 EQ 1 level 5 GE

Check mosaic Ids 6AACF58D95F016C0, 73E60A6DD1D16FDD, and 0015F52218DA969C. It's very straightforward to understand.

Mosaic ID 6A53A4FB36203CCE is different from the others because it adds a reference mosaic. This means that to unlock the fire_spell the player needs to fulfill 2 requirements.

Example code

const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

// (non-reference) mosaic global restriction for mosaic ID: 6AACF58D95F016C0 
const mosaicIdHex = '6AACF58D95F016C0'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(0), // newRestrictionValue
    Symbol.MosaicRestrictionType.GT, // newRestrictionType
    networkType,
    undefined, // reference Mosaic Id
    maxFee,
  );

// (non-reference) mosaic global restriction for mosaic ID: 73E60A6DD1D16FDD 
... 
...
...

// (non-reference) mosaic global restriction for mosaic ID: 0015F52218DA969C
...
...
...


// (non-reference) mosaic global restriction for mosaic ID: 6A53A4FB36203CCE
const mosaicIdHex = '6A53A4FB36203CCE'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('fire_spell'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(2), // newRestrictionValue
    Symbol.MosaicRestrictionType.GE, // newRestrictionType
    networkType,
    undefined, // reference Mosaic Id
    maxFee,
  );

// (reference) mosaic global restriction for mosaic ID: 4 with reference mosaic
const mosaicIdHex = '6A53A4FB36203CCE'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const level_mosaicId = new Symbol.MosaicId('6AACF58D95F016C0');
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(5), // newRestrictionValue
    Symbol.MosaicRestrictionType.EQ, // newRestrictionType
    networkType,
    level_mosaicId, // reference Mosaic Id
    maxFee,
  );

Create Mosaic Address Restriction Transaction

Phew! We have done the mosaic global restriction, now we need to specify which accounts can transact those mosaics. In our case, there are 2 accounts: the dungeon master and the player.

The dungeon master account must have all qualified the global restriction because it needs to transact mosaic to the player account.

const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

// Assign mosaic ID: 6AACF58D95F016C0, key: level, value: 10
const mosaicIdHex = '6AACF58D95F016C0';
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicAddressRestrictionTransaction = Symbol.MosaicAddressRestrictionTransaction.create(
    deadline,
    mosaicId,
    key, // restrictionKey
    dungeon_master.address,
    Symbol.UInt64.fromUint(10), // newRestrictionValue
    networkType,
    Symbol.UInt64.fromHex('FFFFFFFFFFFFFFFF'), // previousRestrictionValue
    maxFee
);

// Assign mosaic ID: 73E60A6DD1D16FDD, key: punch, value: 1
...
...

// Assign mosaic ID: 0015F52218DA969C, key: kick, value: 2
...
...

// Assign mosaic ID: 6A53A4FB36203CCE, key: fire_spell, value: 2
...
...

Let's continue with the player account:

const player = Account.createFromPublicKey(publicKey, networkType);

// Default player level 1
// Assign mosaic ID: 6AACF58D95F016C0, key: level, value: 1
const mosaicIdHex = '6AACF58D95F016C0';
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicAddressRestrictionTransaction = Symbol.MosaicAddressRestrictionTransaction.create(
    deadline,
    mosaicId,
    key, // restrictionKey
    player, // Address
    Symbol.UInt64.fromUint(1), // newRestrictionValue
    networkType,
    Symbol.UInt64.fromHex('FFFFFFFFFFFFFFFF'), // previousRestrictionValue
    maxFee
);

// Default player skill
// Assign mosaic ID: 73E60A6DD1D16FDD, key: punch, value: 1
...
...

// Assign mosaic ID: 0015F52218DA969C, key: kick, value: 0
...
...

// Required upgrade
// Assign mosaic ID: 6A53A4FB36203CCE, key: fire_spell, value: 0
...
...

Initial game

Finally, we have all assets and restrictions set up on the chain.

Dungeon master address stat : TBRNNSHKSOSSIH3ZUGW3MFVAUZ2TBIHL7NE2FVA
Player address stat : TC7FDHJZ53SK5OFUIL2C6X25KQYBXHBCCWCSJGA

Let's transfer assets to the player. The system will detect from a player account on-chain and set players level: 1, punch: active

image.png

Game play

Let's imagine that many possibilities will happen in the game, such as players getting affected by poison, exhausted or power boosted. It could affect player stat in the next round.

Players will be rewarded 1 skill point if they win a round, and they can use it to upgrade skills.

There are a lot of things you can think about to make the game more fun!

How to upgrade and downgrade skills?

The dungeon master can always update the mosaic/address restrictions to control player stats.

Example: player using skill point to upgrade kick.
- Dungeon master can update player address restriction value 0 -> value 2
- Dungeon master transfer mosaicId: 0015F52218DA969C, amount: 1 to player.

Example: player gets the poison status and the punch skill is disabled.
- Dungeon master revokes mosaicId: 73E60A6DD1D16FDD, amount: 1 to the player.

Summary

The mosaics restriction feature is really amazing, there are a lot more things we can do with it.

Setting up on-chain restrictions is such an easy thing on Symbol.

In the beginning it may feel like the restriction feature is complicated, but after you try setting up some of the mosaic restrictions, you will feel it's actually straightforward and easy to understand.

I hope this article will help you understand more about mosaic restriction, and how to use it in your game.

I'm not a game developer, but I definitely love to play games! If you found this idea is cool and you would like to build on it, let me know and we can discuss more in Discord.

If you would like to know more about Symbol, please join the Discord.

Special thanks to Captain Jaguar Gimre Hatchet for provided feedback and help.
Special thanks to Xavi for reviewing this article.

20
5
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
5