create account

Making a Decentralized Game on Hive - Part 4 by mahdiyari

View this thread on: hive.blogpeakd.comecency.com
· @mahdiyari · (edited)
$37.19
Making a Decentralized Game on Hive - Part 4
<center>![coding-pixabay.jpg](https://images.hive.blog/DQmbqyPpfU2dpwYkn9GVJdMmJbfukkvZZ8Bwqk7epyswP9w/macbook-2617705_1280.jpg)</center>
In the [previous part](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-3), we made a back-end that is streaming blocks and detects 3 methods in custom_json. Create a game, request to join a game, and accept the request. We also set up a MySQL server with 3 tables for the methods.

<sub>You can find the links to the other parts at the end of the post.</sub>


### API
Let's start by implementing the API for retrieving the games list. Our API is public so it doesn't require user authentication. The API will just return the data synced from the blockchain.
`api/games.js`:
```
const mysql = require('../helpers/mysql')
const express = require('express')
const router = express.Router()

// Return all games on route /games
router.get('/games', async (req, res) => {
  try {
    // Get games data from Database
    const games = await mysql.query(
      'SELECT `game_id`, `player1`, `player2`, `starting_player`, `status`, `winner` FROM `games`'
    )
    // Check for expected result
    if (!games || !Array.isArray(games) || games.length < 1) {
      // WE return id: 1 for success and id: 0 for errors
      return res.json({
        id: 1,
        games: []
      })
    }
    // We return `games` as we receive from MySQL but it's not a good practice
    // specially when you have critical data in the database
    // You can return data one by one like `games: [{game_id: games.game_id, ...}]`
    return res.json({
      id: 1,
      games
    })
  } catch (e) {
    // Return error for unexpected errors like connection drops
    res.json({
      id: 0,
      error: 'Unexpected error.'
    })
  }
})

module.exports = router
```
The comments are in the code itself for better understanding.
Note: We use `try{} catch{}` wherever we can. It is always good to handle errors.

We can test our API at this point to detect possible errors in the code.
Include the following code in `api/server.js` just above the `port` and `host` constants.
```
const games = require('./games')
app.use(games)
```

Run `node api/server.js`. We can see the console.log: `Application started on 127.0.0.1:2021`
Let's open `127.0.0.1:2021/games` in the browser.

<center>![image.png](https://images.hive.blog/DQmbTKVAbtUB6nDH8HWuvku8NuiZv3jovu94jtEMAEU1vSX/image.png)</center>
The API works as expected.

But it's not done yet. This API returns ALL the games without a specific order. We should implement pagination and define an order for our list.

Updated code `api/games.js`:
```
const mysql = require('../helpers/mysql')
const express = require('express')
const router = express.Router()

router.get('/games/:page', async (req, res) => {
  try {
    if (isNaN(req.params.page)) {
      res.json({
        id: 0,
        error: 'Expected number.'
      })
    }
    const page = Math.floor(req.params.page)
    if (page < 1) {
      res.json({
        id: 0,
        error: 'Expected > 0'
      })
    }
    const offset = (page - 1) * 10
    const games = await mysql.query(
      'SELECT `game_id`, `player1`, `player2`, `starting_player`, `status`, `winner` FROM `games`' +
        ' ORDER BY `id` DESC LIMIT 10 OFFSET ?',
      [offset]
    )
    if (!games || !Array.isArray(games) || games.length < 1) {
      return res.json({
        id: 1,
        games: []
      })
    }
    return res.json({
      id: 1,
      games
    })
  } catch (e) {
    res.json({
      id: 0,
      error: 'Unexpected error.'
    })
  }
})

module.exports = router
```
We used `id` to order our list and get the newly created games first. Each page returns up to 10 games. We can try `127.0.0.1:2021/games/1` for testing.
***
Let's set another API for requests. The code is almost similar but we return only requests for the specific game_id.
`api/requests.js`:
```
const mysql = require('../helpers/mysql')
const express = require('express')
const router = express.Router()

router.get('/requests/:id', async (req, res) => {
  try {
    if (!req.params.id) {
      res.json({
        id: 0,
        error: 'Expected game_id.'
      })
    }
    // We are passing user input into the database
    // You should be careful in such cases
    // We use ? for parameters which escapes the characters
    const requests = await mysql.query(
      'SELECT `player`, `status` FROM `requests` WHERE `game_id`= ?',
      [req.params.id]
    )
    if (!requests || !Array.isArray(requests) || requests.length < 1) {
      return res.json({
        id: 1,
        requests: []
      })
    }
    return res.json({
      id: 1,
      requests
    })
  } catch (e) {
    res.json({
      id: 0,
      error: 'Unexpected error.'
    })
  }
})

module.exports = router
```
Note: `:id` in the above router represents a variable named id. So for example `http://127.0.0.1:2021/requests/mygameidhere` in this request, the `id` variable is `mygameidhere` which is accessible by `req.params.id`.
***
A similar code for the `moves` table. There wasn't a better name in my mind for this table.
`api/moves.js`:
```
const mysql = require('../helpers/mysql')
const express = require('express')
const router = express.Router()

router.get('/moves/:id', async (req, res) => {
  try {
    if (!req.params.id) {
      res.json({
        id: 0,
        error: 'Expected game_id.'
      })
    }
    const moves = await mysql.query(
      'SELECT `player`, `col`, `row` FROM `moves` WHERE `game_id`= ?',
      [req.params.id]
    )
    if (!moves || !Array.isArray(moves) || moves.length < 1) {
      return res.json({
        id: 1,
        moves: []
      })
    }
    return res.json({
      id: 1,
      moves
    })
  } catch (e) {
    res.json({
      id: 0,
      error: 'Unexpected error.'
    })
  }
})

module.exports = router
```
Now our 3 APIs are ready to be implemented on the front-end.
***
Here is the updated `api/server.js` after including the APIs:
```
const express = require('express')
const bodyParser = require('body-parser')
const hpp = require('hpp')
const helmet = require('helmet')
const app = express()

// more info: www.npmjs.com/package/hpp
app.use(hpp())
app.use(helmet())

// support json encoded bodies and encoded bodies
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.use(function (req, res, next) {
  res.header(
    'Access-Control-Allow-Origin',
    'http://localhost https://tic-tac-toe.mahdiyari.info/'
  )
  res.header('Access-Control-Allow-Credentials', true)
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept, access_key'
  )
  next()
})

// APIs
const games = require('./games')
const requests = require('./requests')
const moves = require('./moves')

app.use(games)
app.use(requests)
app.use(moves)

const port = process.env.PORT || 2021
const host = process.env.HOST || '127.0.0.1'
app.listen(port, host, () => {
  console.log(`Application started on ${host}:${port}`)
})
```
***
### Front-end
I think using pure HTML is a mistake and I would prefer something like Angular for the web applications but that comes with its own learning process which can make this tutorial complex. So my recommendation is to learn something like Angular or Vue and live a happy life. Anyway, coding time.

I'm not going to drop `index.html` here. It doesn't need much explanation and it's long. You can see it on the [GitLab repository](https://gitlab.com/mahdiyari/decentralized-game-on-hive/-/blob/master/front-end/index.html). I will just add some references here used in `app.js`.

The table for listing the games and buttons for pagination.
`index.html`:
```
<div class="card-body">
  <h5 class="card-title" style="float: left;">Games list</h5>
  <span style="float: right;">Auto updating every 5s</span>
  <table class="table table-striped">
    <thead>
      <tr>
        <th>#</th>
        <th>Game ID</th>
        <th>Player 1</th>
        <th>Player 2</th>
        <th>Starting Player</th>
        <th>Status</th>
        <th>Winner</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody id="games-table-body">
    </tbody>
  </table>
  <nav aria-label="Page navigation example">
    <ul class="pagination justify-content-center">
      <li class="page-item disabled" id="prev-btn">
        <a class="page-link" onclick="prevGamesPage()">&laquo;</a>
      </li>
      <li class="page-item disabled">
        <a class="page-link" id="page-number" tabindex="-1"> 1 </a>
      </li>
      <li class="page-item" id="next-btn">
        <a class="page-link" onclick="nextGamesPage()">&raquo;</a>
      </li>
    </ul>
  </nav>
</div>
```
***
We have to fill the table above. So let's implement some basic functions.
`js/app.js`:
```
const baseAPI = 'http://127.0.0.1:2021'
const APICall = async (api) => {
  return (await fetch(baseAPI + api)).json()
}
```
For ease of use, we define a function for `GET` calls using `fetch` and a variable for our API address.
***
```
const getGames = async (page = 1) => {
  const games = await APICall('/games/' + page)
  return games.games
}
```
This function basically gets the games from the API per page.
***
```
const fillGamesTable = (data) => {
  const tbody = document.getElementById('games-table-body')
  let temp = ''
  for (let i = 0; i < data.length; i++) {
    temp += `<tr>
    <td>${(gamesPage - 1) * 10 + i + 1}</td>
    <td>${data[i].game_id}</td>
    <td>${data[i].player1}</td>
    <td>${data[i].player2}</td>
    <td>${data[i].starting_player}</td>
    <td>${data[i].status}</td>
    <td>${data[i].winner}</td>
    <td></td>
    </tr>`
  }
  if (data.length < 1) {
    temp = 'No games.'
  }
  tbody.innerHTML = temp
}
```
`fillGamesTable` takes the result from `getGames` function and fills the HTML table with data using a `for` loop.
***
```
let gamesPage = 1
const loadTheGames = async () => {
  const games = await getGames(gamesPage)
  fillGamesTable(games)
  if (games.length < 10) {
    document.getElementById('next-btn').className = 'page-item disabled'
  } else {
    document.getElementById('next-btn').className = 'page-item'
  }
  if (gamesPage === 1) {
    document.getElementById('prev-btn').className = 'page-item disabled'
  } else {
    document.getElementById('prev-btn').className = 'page-item'
  }
  document.getElementById('page-number').innerHTML = ` ${gamesPage} `
}
loadTheGames()
setInterval(() => loadTheGames(), 5000)
```
With this function, we call the two previously defined functions to do their job and update the pagination buttons and the page number every time we update the table data. Also, every 5 seconds, it gets new data from API and updates the table with new data so users don't have to reload the page for new data.
***
```
const nextGamesPage = () => {
  gamesPage++
  loadTheGames()
}

const prevGamesPage = () => {
  gamesPage--
  loadTheGames()
}
```
And two functions for changing pages. Simple as that.
***
The final result with dummy data looks like this on the browser:
<center>![Capture.PNG](https://images.hive.blog/DQmcYZC1vixajRW4Ui5dHiSfTy5MYqEYZhXy2bd2ituz4Ar/Capture.PNG)</center>
***
That's it for this part. I'm really happy with the results we are getting. I didn't plan anything beforehand and I'm coding as I'm writing the posts.

Today we made 3 API calls and created the basic front-end which for now only shows the list of games that are submitted to the Hive blockchain. In the next part, we will implement the methods for creating and joining the games on the client side. We also probably need to create a game page where actual gaming happens.

Thanks for reading. Make sure to follow me and share the post. Upvote if you like and leave a comment.
***
[GitLab](https://gitlab.com/mahdiyari/decentralized-game-on-hive)
[Part 1](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-tic-tac-toe-part-1)
[Part 2](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-2)
[Part 3](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-3)

[Next part >>](https://hive.blog/hive-169321/@mahdiyari/making-a-decentralized-game-on-hive-part-5)
***
**Vote for my witness:**
- https://wallet.hive.blog/~witnesses
- https://peakd.com/witnesses
- https://ecency.com/witnesses
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 431 others
properties (23)
authormahdiyari
permlinkmaking-a-decentralized-game-on-hive-part-4
categoryhive-169321
json_metadata{"tags":["dev","gamedev","development","game","technology"],"image":["https://images.hive.blog/DQmbqyPpfU2dpwYkn9GVJdMmJbfukkvZZ8Bwqk7epyswP9w/macbook-2617705_1280.jpg","https://images.hive.blog/DQmbTKVAbtUB6nDH8HWuvku8NuiZv3jovu94jtEMAEU1vSX/image.png","https://images.hive.blog/DQmcYZC1vixajRW4Ui5dHiSfTy5MYqEYZhXy2bd2ituz4Ar/Capture.PNG"],"links":["https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-3"],"app":"hiveblog/0.1","format":"markdown"}
created2021-04-01 17:09:48
last_update2021-04-11 17:11:45
depth0
children7
last_payout2021-04-08 17:09:48
cashout_time1969-12-31 23:59:59
total_payout_value20.433 HBD
curator_payout_value16.758 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length12,320
author_reputation199,864,818,197,856
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,750,281
net_rshares40,482,285,195,788
author_curate_reward""
vote details (495)
@dwinblood · (edited)
$0.09
I might be interested in adding HIVE to some of my games.  Though I'd likely not do any of them as web page form.  Thanks for making this series it might come in useful.   I will definitely give it some more thought.  I would be doing any work with this in C#.
👍  
properties (23)
authordwinblood
permlinkre-mahdiyari-qqy3z7
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"peakd/2021.03.9"}
created2021-04-02 16:47:33
last_update2021-04-02 16:48:33
depth1
children0
last_payout2021-04-09 16:47:33
cashout_time1969-12-31 23:59:59
total_payout_value0.054 HBD
curator_payout_value0.031 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length260
author_reputation383,232,067,634,988
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,772,020
net_rshares200,833,157,941
author_curate_reward""
vote details (1)
@ganjafarmer ·
$0.11
Cool man I completely don't read any of this but I really am excited to see the introduction of more games here
👍  , , , ,
properties (23)
authorganjafarmer
permlinkre-mahdiyari-qqwbj0
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"peakd/2021.03.9"}
created2021-04-01 17:35:24
last_update2021-04-01 17:35:24
depth1
children0
last_payout2021-04-08 17:35:24
cashout_time1969-12-31 23:59:59
total_payout_value0.056 HBD
curator_payout_value0.055 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length111
author_reputation672,900,775,922,025
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,750,774
net_rshares203,944,885,794
author_curate_reward""
vote details (5)
@nathanmars ·
$0.10
Quick question. 

We wanted HIVE to get listed on coinbase soon.. we need our hive code to be written in a special language apparently. Do you have any idea regarding this please ?
👍  
properties (23)
authornathanmars
permlinkre-mahdiyari-qqwba0
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"peakd/2021.03.9"}
created2021-04-01 17:30:00
last_update2021-04-01 17:30:00
depth1
children3
last_payout2021-04-08 17:30:00
cashout_time1969-12-31 23:59:59
total_payout_value0.052 HBD
curator_payout_value0.052 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length180
author_reputation336,354,946,115,368
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,750,653
net_rshares190,582,250,680
author_curate_reward""
vote details (1)
@mahdiyari ·
I don't know for sure but I'll ask around.
👍  
properties (23)
authormahdiyari
permlinkqqwbel
categoryhive-169321
json_metadata{"app":"hiveblog/0.1"}
created2021-04-01 17:32:45
last_update2021-04-01 17:32:45
depth2
children0
last_payout2021-04-08 17:32:45
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length42
author_reputation199,864,818,197,856
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,750,710
net_rshares12,661,950,476
author_curate_reward""
vote details (1)
@mahdiyari ·
Can you say who are you working with on this?
I got this from @ausbitbank
>We'd need to build a "rosetta" interface to work on coinbase :
https://github.com/coinbase/rosetta-specifications
https://www.rosetta-api.org/
👍  
properties (23)
authormahdiyari
permlinkqqwcuv
categoryhive-169321
json_metadata{"users":["ausbitbank"],"links":["https://github.com/coinbase/rosetta-specifications"],"app":"hiveblog/0.1"}
created2021-04-01 18:04:06
last_update2021-04-01 18:04:06
depth2
children1
last_payout2021-04-08 18:04:06
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length217
author_reputation199,864,818,197,856
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,751,294
net_rshares12,647,866,539
author_curate_reward""
vote details (1)
@nathanmars ·
I'm not working on this and wanted to make this happen

@brettpullen wants to work on this
properties (22)
authornathanmars
permlinkqqxtzy
categoryhive-169321
json_metadata{"users":["brettpullen"],"app":"hiveblog/0.1"}
created2021-04-02 13:12:00
last_update2021-04-02 13:12:00
depth3
children0
last_payout2021-04-09 13:12:00
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length90
author_reputation336,354,946,115,368
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,767,973
net_rshares0
@poshbot ·
https://twitter.com/MahdiYari4/status/1377673640146317314
properties (22)
authorposhbot
permlinkre-making-a-decentralized-game-on-hive-part-4-20210401t172648z
categoryhive-169321
json_metadata"{"app": "beem/0.24.20"}"
created2021-04-01 17:26:48
last_update2021-04-01 17:26:48
depth1
children0
last_payout2021-04-08 17:26:48
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length57
author_reputation5,554,335,374,496
root_title"Making a Decentralized Game on Hive - Part 4"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id102,750,591
net_rshares0