![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