We will develop a decentralized game by the end of this series. The goal is to make learning development on Hive blockchain easier by using a simple game as the training project.
## Target audience
Of course, it's not for everybody. I will try to keep it simple as possible for people with less experience with development. But some degree of understanding code is necessary to learn something. I will explain anything related to the Hive blockchain that we use.
You can find the links for the final result of this part at the end of this post.
## Why "tic-tac-toe"?
It's a simple multiplayer game and it covers most of the topics needed for a bigger decentralized game or application. Most people are already familiar with this game and it's easier to code and its rules are simple.
## Development
We will use Javascript for the game because it's simple enough so most people understand easily. The front-end will be pure HTML. Also, I think MySQL is a good fit as the database. The game needs a database to keep track of games and players. MySQL docker can be set up in a few minutes.
The decentralized game will work without depending on one central software. The game will talk only to the blockchain and there is no central database. It doesn't need a private entity to hold players' data. Anyone can run an interface for the game.
(We have a database but it's not a central private database and it can be synced through blockchain. Anyone should be able to run an instance of the game and the game will get the same exact database by reading data from the blockchain. It's like hivemind, the database that holds and serves most of the data on Hive.)
## Tools I'm using
- Visual Studio Code
- Nodejs
- MySQL docker setup
- Chrome browser
I didn't plan anything beforehand so I don't know how many posts it will take. I will list the things that come to my mind right now.
## Planning
- Front-end
- Make a login method on the client-side
- Display available games list
- Create/Request to join a game
- Design the game visuals and controls
- Back-end
- Stream the blockchain and listen for custom_json operations
- Define custom_json operations
- Game mechanics
- API to communicate with front-end
- Replay/resync method to update the database on newly deployed game clients
The above list may or may not change. Anyway, let's start with the front-end and add a login method.
***
## Part 1: Front-end - Login method
I think the HTML part doesn't need any explanation. Page title, description, bootstrap navbar, and a login link. Added `css/style.css` and `js/app.js` too. When the user clicks on the "login" link, a modal with the login form will appear. It will then fire `login()` function on submit.
`index.html`:
```
<!DOCTYPE html>
Tic-Tac-Toe on Hive blockchain
Login
```
***
### Styles
`css/style.css`:
```
.navbar-nav {
margin-right: 30px;
}
.navbar-brand {
margin-left: 30px;
}
#login-error {
color: #e31337;
display: none;
}
```
***
## Javascript
`js/app.js`
An object for user data.
```
const userData = {
authorized: false,
username: '',
key: ''
}
```
Let's define the login function. It will verify the posting key and username then keep the data in localStorage. We use the [hive-tx](https://github.com/mahdiyari/hive-tx-js) library for converting keys.
```
const login = async () => {
const loginModal = bootstrap.Modal.getInstance(
document.getElementById('login-modal')
)
const loginButtonForm = document.getElementById('login-form-btn')
loginButtonForm.setAttribute('disabled', 'true')
const loginError = document.getElementById('login-error')
loginError.style.display = 'none'
const username = document.getElementById('username').value
const key = document.getElementById('posting-key').value
const validate = await validatePostingKey(username, key)
if (validate.result === 0) {
loginError.innerHTML = validate.error
loginError.style.display = 'block'
loginButtonForm.removeAttribute('disabled')
return
}
userData.authorized = true
userData.username = username
userData.key = key
window.localStorage.setItem('userData', JSON.stringify(userData))
loginButtonForm.removeAttribute('disabled')
updateState()
loginModal.hide()
}
```
As you can see there are 2 more functions used inside the login function. The first one is `validatePostingKey()`. It's a post for itself.
Let me explain it. First, we make a call to the Hive RPC API node by using hive-tx library and get the account information including the public posting key. Then we convert the user's private posting key to the public key and compare the two values. If the two values are equal then the user-provided private key is correct and we can authorize the user. There are also other ways to achieve the same result like signing a message with the private key and validating that signature with the public key.
```
const validatePostingKey = async (username, privateKey) => {
const accounts = await hiveTx.call('condenser_api.get_accounts', [[username]])
if (
!accounts ||
!accounts.result ||
!Array.isArray(accounts.result) ||
accounts.result.length < 1
) {
return { result: 0, error: 'Network error or wrong username' }
}
try {
const account = accounts.result[0]
const publicWif = account.posting.key_auths[0][0] || ''
const generatedPublicKey = hiveTx.PrivateKey.from(privateKey)
.createPublic()
.toString()
if (generatedPublicKey !== publicWif) {
return { result: 0, error: 'Wrong key' }
}
return { result: 1 }
} catch (e) {
return { result: 0, error: 'Wrong key or network error' }
}
}
```
updateState() is used to update the HTML interface after user login and logout.
```
const updateState = () => {
const loginButton = document.getElementById('login-button')
const logoutMenu = document.getElementById('logout-menu')
const usernameButton = document.getElementById('username-button')
if (userData.authorized && userData.username && userData.key) {
loginButton.style.display = 'none'
logoutMenu.style.display = 'block'
usernameButton.innerHTML = '@' + userData.username
} else {
loginButton.style.display = 'block'
logoutMenu.style.display = 'none'
}
}
```
And it's time for the logout function.
```
const logout = () => {
userData.authorized = false
userData.username = ''
userData.key = ''
window.localStorage.removeItem('userData')
updateState()
}
```
We need to check localStorage on the page reload and log in the user if the key is in the localStorage.
```
const checkState = () => {
const localData = window.localStorage.getItem('userData')
let data
if (!localData) {
return
}
try {
data = JSON.parse(localData)
} catch (e) {
data = userData
}
if (data.authorized && data.username && data.key) {
userData.authorized = true
userData.username = data.username
userData.key = data.key
updateState()
}
}
checkState()
```
Now we have a working login and logout system. It keeps user data in localStorage which stays on the browser only.
What we have done so far is just the front-end. Our game needs a back-end server to provide the game data. We will stream blocks on the back-end and process game data then serve it through API. Our front-end will broadcast transactions which will update the back-end. In other words, the back-end is only serving the data it receives through the blockchain. We could stream the blocks on the client-side (browser) but it's not efficient and it is just unnecessary bandwidth waste.
***
You can see the running app on https://tic-tac-toe.mahdiyari.info/
The final code is on GitLab https://gitlab.com/mahdiyari/decentralized-game-on-hive
In the next part, we will set up our back-end server and database. Now that I think about it, MySQL might be an overkill for this project. Anyway, let's stick to it for now.
I want to get feedback from the community before continuing further. Help me with your comments and let me know what you think about this project. How can I improve it? Should I explain everything from the basics? I greatly appreciate your feedback.
Thanks for reading.
[Next part >>](https://hive.blog/hive-139531/@mahdiyari/making-a-decentralized-game-on-hive-part-2)