![code-944499_1280.jpg](https://images.hive.blog/DQmZn8iD8SQbqnN8RNGJku4SCKQH6pKAPBh6QqtBrby1mAN/code-944499_1280.jpg)
Previously in [part 1](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-tic-tac-toe-part-1), we made a simple front-end and a login option with posting key.
In this part, we work on the back-end of the game. We use [Nodejs](https://nodejs.org/en/) for running our back-end codes. I assume you know how to create or run a Nodejs app. It's not complicated and I will cover most of it here.
## API server
`api/server.js` is the starting point of the API server. Let's initialize it with expressjs and some libraries for API usage.
```
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()
})
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}`)
})
```
***
**EDIT: added this part later!**
I made a mistake in the above code and apparently, multiple values are not allowed in the `Access-Control-Allow-Origin` header. So I modified the code as below:
```
app.use(function (req, res, next) {
const allowedOrigins = [
'http://localhost',
'https://tic-tac-toe.mahdiyari.info/'
]
const origin = req.headers.origin
if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin)
}
res.header('Access-Control-Allow-Credentials', true)
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, access_key'
)
next()
})
```
**Edit end.**
***
Don't forget to install npm packages.
```
npm install express
npm install body-parser
npm install hpp
npm install helmet
```
`hpp` and `helmet` are for increased security and `body-parser` for parsing request bodies for json encoded bodies and encoded bodies.
I also added `http://localhost` and `https://tic-tac-toe.mahdiyari.info/` to the `Access-Control-Allow-Origin` header. You can add another URL to receive API calls from or just put `*`. It basically limits the usage of the API server to the listed URLs.
Our API server will listen to `127.0.0.1:2021` by default. It does nothing at the moment. Let's continue with the main application.
***
## Main application
We will run 2 Nodejs apps. One is the API server and the other is the main application where streaming blocks and processing data is happening. The reason for splitting applications is to run the API server in cluster mode. With cluster mode, we can run one API server for each CPU core. It will help with load balancing and keep API running as fast as possible while serving many requests. The cluster mode is useful only on API servers and especially Expressjs apps.
We will need a helper to stream the blocks.
`helpers/streamBlock.js`:
```
const hiveTx = require('hive-tx')
const INTERVAL_TIME = 1000
const streamBlockNumber = async (cb) => {
try {
let lastBlock = 0
setInterval(async () => {
const gdgp = await hiveTx.call(
'condenser_api.get_dynamic_global_properties'
)
if (
gdgp &&
gdgp.result &&
gdgp.result.head_block_number &&
!isNaN(gdgp.result.head_block_number)
) {
if (gdgp.result.head_block_number > lastBlock) {
lastBlock = gdgp.result.head_block_number
cb(lastBlock)
}
}
}, INTERVAL_TIME)
} catch (e) {
throw new Error(e)
}
}
const streamBlockOperations = async (cb) => {
try {
streamBlockNumber(async (blockNumber) => {
const result = await hiveTx.call('condenser_api.get_block', [
blockNumber
])
if (result.result) {
const operations = result.result.transactions.map((transaction) => {
return transaction.operations
})
if (operations.length > 0) {
for (const operation of operations) {
cb(operation)
}
}
}
})
} catch (e) {
throw new Error(e)
}
}
module.exports = {
streamBlockNumber,
streamBlockOperations
}
```
install [hive-tx](https://www.npmjs.com/package/hive-tx): `npm install hive-tx`
We created 2 functions here. The first one `streamBlockNumber` makes a call to get `dynamic_global_properties` every `INTERVAL_TIME` which I set to 1000ms (1 second) then checks for newly produced block number. If the block number is increased, then it calls the callback function with the new block number. It's a helpful function for getting newly generated block numbers.
We use the first function inside the second function `streamBlockOperations` to get newly generated blocks and get operations inside that block by using the `condenser_api.get_block` method.
`streamBlockOperations` will call the callback function with newly added operations to the blockchain (except virtual operations).
`index.js`:
```
const stream = require('./helpers/streamBlock')
try {
stream.streamBlockOperations((ops) => {
if (ops) {
const op = ops[0]
if (op && op[0] === 'custom_json' && op[1].id === 'tictactoe') {
processData(op[1].json)
}
}
})
} catch (e) {
throw new Error(e)
}
```
This will stream newly added operations to the blockchain and send the JSON from `custom_json` operations with the id of `tictactoe` to the `processData` function.
***
We should define game mechanics and their corresponding custom_json.
**Create a game**
```
{
app: 'tictactoe/0.0.1'
action: 'create_game',
id: 'Random generated id',
starting_player: 'first or second'
}
```
Create a game and wait for the other player to join.
***
**Request join a game**
```
{
app: 'tictactoe/0.0.1',
action: 'request_join',
id: 'Game id'
}
```
Request to join an open game which is created by someone else.
***
**Accept join request**
```
{
app: 'tictactoe/0.0.1',
action: 'accept_request',
id: 'Game id',
player: 'username'
}
```
Accept the pending join request from another player to your created game.
***
**Play**
```
{
app: 'tictactoe/0.0.1',
action: 'play',
id: 'Game id',
col: '1 to 3',
row: '1 to 3'
}
```
Play or place an X/O on the board. `col` is the column and `row` is for the row of the placed X/O on the board.
![tic-tac-toe-col-row.jpg](https://images.hive.blog/DQmYtN2du2ngnu51fernMHqvmp5VKSyZ5JkF6wRgS9MkubP/tic-tac-toe-col-row.jpg)
***
Code implamantaion of the above in `index.js`:
```
const processData = (jsonData) => {
try {
if (!jsonData) {
return
}
const data = JSON.parse(jsonData)
if (!data || !data.action || !data.app) {
return
}
if (data.action === 'create_game') {
createGame(data)
} else if (data.action === 'request_join') {
requestJoin(data)
} else if (data.action === 'accept_request') {
acceptRequest(data)
} else if (data.action === 'play') {
play(data)
}
} catch (e) {
// error might be on JSON.parse and wrong json format
return null
}
}
```
***
Let's create a function for each game mechanic.
**createGame**:
```
const createGame = (data) => {
if (!data || !data.id || !data.starting_player) {
return
}
// validating
if (
data.id.length !== 20 ||
(data.starting_player !== 'first' && data.starting_player !== 'second')
) {
return
}
// Add game to database
console.log('Create a game with id ' + data.id)
}
```
***
**requestJoin**:
```
const requestJoin = (data) => {
if (!data || !data.id || !data.id.length !== 20) {
return
}
// Check game id in database
// Join game
console.log('A player joined game id ' + data.id)
}
```
***
**acceptRequest**:
```
const acceptRequest = (data) => {
if (!data || !data.id || !data.player || !data.id.length !== 20) {
return
}
// Validate game in database
// Accept the join request
console.log('Accepted join request game id ' + data.id)
}
```
***
**play**:
```
const play = (data) => {
if (
!data ||
!data.id ||
!data.col ||
!data.row ||
!data.id.length !== 20 ||
data.col < 1 ||
data.col > 3 ||
data.row < 1 ||
data.row > 3
) {
return
}
// Validate game in database
// Validate the player round
// Play game
console.log('Played game id ' + data.id)
}
```
***
The above functions are not doing anything at the moment. We will complete those functions after setting up the database in the next part.
We can test our code by broadcasting custom_json operations. Let's see if the streaming method and processing data works.
We can run the app by `node index.js`
https://hiveblocks.com/tx/44799e6a27c64e935f9072ecb576602330cb80b8
![image.png](https://images.hive.blog/DQmSfMrgDbSPjUPWaPeekrWfDFTS6zfdkk4NMySjNQmh3xh/image.png)
And here is the console.log() confirmation in our app:
![image.png](https://images.hive.blog/DQmb4kymGnacj2H9D4f2jfU8xYWJdjcd1EKDZeLN6q96q8h/image.png)
***
Follow me so you don't miss the next part. The final code of this part is on GitLab.
[GitLab Repository](https://gitlab.com/mahdiyari/decentralized-game-on-hive)
[Website](https://tic-tac-toe.mahdiyari.info/)
[Part 1](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-tic-tac-toe-part-1)
[Next part >>](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-3)
See: Making a Decentralized Game on Hive - Part 2
by
@mahdiyari