1

I'm trying to build a small hearthstone like game and I've finished up the front-end for the game client. I created a NoSQL mockup of the user data and now it's time to move those to the index_table in smart contract code.

Here is how the user data template looks like in my front-end (typescript):

interface User {
  name: string,
  xp: number,
  lv: number,
  avatar: string,
  isInGame: boolean,
  selectedDeckName: string,
  redShards: number,
  blueShards: number,
  cards: {
    solid: Card[],
    liquid: Card[],
    gas: Card[],
    plasma: Card[],
    neutral: Card[]
  }
}

interface Card {
   name: string,
   selectedSkinName: string,
   skins: CardSkin[]
}

interface CardSkin {
   name: string,
   owned: number
}

so I implemented the index_table like this in smart contract:

struct [[eosio::table]] user {
  name username;
  int xp;
  int lv;
  string avatar;
  bool isInGame;
  string selectedDeckName;
  int redShards;
  int blueShards;
  uint64_t primary_key() const {return username.value;}
};

Apart from cards there is also a decks object which also has nested arrays of objects inside that keeps track of decks users create but I didn't include them here for simplicity, since same logic applies to both card and deck objects.

And i managed to insert some data with cleos and fetch it with the eosjs lib to my frontend. But now I'm stuck with the cards object, which has 5 arrays nested inside and all those 5 arrays should be arrays of Card objects, and it nests pretty deep. I'm so used to MongoDB and noSQL but this index_table feels like SQL to me (it is a table indeed), so maybe this is why I'm struggling.

How would you guys approach this? The only thing that comes to mind is 60 tables (12 cards for each class) but doesn't feel like the right solution to me.

I'm not sure whether this question has any sense but I had nowhere else to turn. Thanks in advance!

1 Answer 1

1

Without knowing the exact game logic I can't really answer your question but the following should at least show you how to nest objects.

struct [[eosio::table]] user_t {

  struct card_skin_t {
    eosio::name               name;
    uint8_t                   owned;
  }

  struct card_t {
   eosio::name                card_name;
   eosio::name                selected_skin_name;
   std::vector<card_skin_t>   skins;
  }

  struct cards_t {
    std::vector<card_t>       solid;
    std::vector<card_t>       liquid;
    std::vector<card_t>       gas;
    std::vector<card_t>       plasma;
    std::vector<card_t>       neutral;
  }

  eosio::name     username;
  uint16_t        xp;
  uint16_t        lv;
  eosio::name     avatar;
  bool            is_in_game;
  eosio::name     selected_deck_name;
  uint16_t        red_shards;
  uint16_t        blue_shards;
  
  cards_t        cards;
  
  uint64_t primary_key() const {return username.value;}
};

Additionally a few tips and infos:

  • Don't use strings if it's possible to store the info in a eosio::name. Strings consume a lot of RAM and are therefore very expensive. In addition, eosio::name's can be used as a key and thus as a reference, which is not so easy with strings.
  • Try to use the most useful datatypes in your structures/tables - choosing int as datatype for red-shards for example doesn't look clever as I guess a user can't have a negative number of red shards and probably the possibility to have UINT8_MAX (255) or UINT16_MAX (65535) and not INT32_MAX (2147483647) red shards is enough
  • If you want to have an array-like structure, you usually use the std::vector type (other container types are possible but I'll leave it at that). In a transaction you use std::vector's like a normal json array. Take a look here to learn how to use vectors from within your contract.
  • If you use multiple bools, wrap them together into another primitive type (1 byte = 8 bool), otherwise each bool consumes one byte of RAM.
  • Follow common naming conventions to keep your code clean.
4
  • 1
    Wow, my mind is blown :D There is a lot more to types than I thought! Shards are capped at 100 after which they reset so int32 is definitely a big waste... The answer is exactly what I was looking for, should be enough to bootstrap me and allow me to continue forward on my own. I thought I'd keep the camelCase for tables so I can easily destructure the properties on the front end but I will start using the c++ / eos conventions. I know little about vectors tho but I do know they are similar to js arrays... I will use your code and write the rest following your rules. Thank you again!! ^^ Commented Jan 26, 2021 at 17:52
  • 2
    No problem, always a pleasure to help other devs finding their way into eosio. Feel free to contact me on telegram if you are stuck somewhere.
    – cmadh
    Commented Jan 27, 2021 at 0:12
  • How can I find you on telegram? I don't use it very often... I already have more questions, now that I know the basics looks like there's lots of things that need changing and refactoring... Commented Jan 27, 2021 at 15:59
  • My tg-name is cmadh also. Just search for my name and you should find me.
    – cmadh
    Commented Jan 27, 2021 at 22:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.