DOCUMENTS

Documents of the BORA project.

Game Example: Pacman

게임 예제는 Angular 및 Node.js 서버로 구성 되어 있습니다. 소개하는 예제를 이해하기 위해서는 Javascript, Typescript에 대한 사전 지식이 필요합니다. 다만, Javascript 전문 개발자가 아니더라도 이해 하실 수 있도록 설명이 첨부되어 있으니 일반적인 Application 제작 경험이 있는 경우 이해 하실 수 있도록 구성 되어 있습니다.

목표

멤버십 인증

기존의 오픈 소스 웹 게임에 BORA 멤버십을 연동, 가입과 로그인을 경험한다.

실시간 BORA Shell 전송

게임 진행 중 점수를 획득할 때마다, 사용자에게 BORA Shell을 실시간으로 지급한다.

BORA 체인 내 BORA Shell 전송 정보 확인

BORA Chain 내에 저장된 BORA Shell 전송 내역을 실시간으로 확인한다.

구성

게임 영역

게임을 플레이 할 수 있는 영역

BORA Chain 정보 영역

게임과 연동된 BORA Chain에 기록된 정보를 확인할 수 있는 영역
  • My BORA Shell : 사용자 계정의 지갑 주소와 토큰 보유 수량
  • BORA Tower Blocks SCORE : 게임 점수 고득점 기준 순위 정보
  • BORA Explorer Recent Blocks : BORA Chain의 실시간 블록 정보
  • BORA Explorer Recent Transactions : 최신 Transaction 목록

예제 소스 코드 살펴보기

우선 git에서 소스 코드를 확인하시기 바랍니다. 게임 로직에 대한 설명은 하지 않으나, BORA 플랫폼과 연동한 부분에 대하여 하단에서 설명합니다.Game Server와 Game Front를 직접 구동하고 싶으신 분은, 아래에 설명하는 github에서 상세한 구동 방법을 확인할 수 있습니다.

게임 서버 및 게임 프론트

오픈 소스 게임 https://github.com/masonicGIT/pacman 을 Node.js 서버 위에 올렸습니다. Node.js 서버에 올리기 위한 설정을 제외하면, 획득한 점수를 블록체인에 기록하기 위해 최소한의 수정 만을 가했습니다- pacman.js 의 score을 올리는 부분에 saveScore 기능만을 추가했습니다.
  • app.js : Node.js 서버를 구동하기 위한 부분
  • main.js : 게임과 게임 서버간 통신을 위한 function을 정의
  • chainApi.js : BORA 플랫폼에서 제공하는 Chain API 호출
  • pacman.js : 게임내의 소스 중 점수를 획득할 때 saveScore 함수를 호출 하는 부분을 참고하십시오. 단 한 줄입니다.
Game Server: https://github.com/boraecosystem/game-pacman Game Front: https://github.com/boraecosystem/game-front

동작 시나리오

상기 언급한 목표 즉, BORA Shell를 실시간 지급하기 위해서는 지급 대상인 사용자의 주소 정보와 BORA Shell 제공자인 콘텐츠 제공자의 인증정보가 필요합니다.

사용자의 주소 정보 얻어오기

본 게임에서 사용자로 부터 인증을 받는 목적은 유저의 주소 정보를 얻기 위함입니다. 사용자는 해당 게임을 이용하는 end-user를 의미합니다.
  • OAuth 2.0 Authorization Code Grant 방식의 멤버십 인증 페이지를 통해 사용자로부터 인증(authorization) 받음
  • 이후 취득한 code로 부터 access token을 획득
  • access token을 이용해서 주소 확인 API를 호출하여 서비스용 주소 정보를 취득

사용자의 멤버십 인증

Access Token이 없거나 만료된 경우, BORA에서 제공하는 인증 창을 호출 하면 BORA의 인증 서버에서는 아래와 같은 과정이 진행됩니다.
  • ID/PW 입력창 팝업에서 사용자 로그인
  • APP 권한 확인: 권한이 없는 경우, 권한 상승 질의 및 승인
  • Access Token 발급하기 위한 code 발급
// https://github.com/boraecosystem/game-front/blob/master/src/app/components/main/main.component.ts#L48

if (this.accessToken === undefined || !this.accessToken) {
// redirect to auth//
window.location.href = environment.api + 'member/oauth/authorize' + '?response_type=code&state=xyz&client_id=' + this.clientId + '&redirect_uri=' + this.redirectUri;
return false;
}

Access token을 요청

// https://github.com/boraecosystem/game-pacman/blob/master/app.js#L118

const httpOAuth = require('http');
const data = `grant_type=authorization_code&code=${code}&redirect_uri=${redirectUri}`;
function getUserToken(data, cb) {
const req = httpOAuth.request({
protocol: 'http:',
host: api_uri,
port: 443,
method: 'POST',
path: '/member/oauth/token',
auth: `${clientId}:${clientSecret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}, res => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => cb(JSON.parse(body)));
}
);
req.on('error', e => console.error(`problem with request: ${e.message}`));
req.write(data);
req.end();
}

사용자의 주소 획득

발급 받은 Access Token을 이용하여, 사용자의 주소를 확인하는 API를 호출합니다.
// https://github.com/boraecosystem/game-front/blob/master/src/app/components/auth/auth.component.ts#L40

this.api.getMembers(accessToken).subscribe(
resMember => {
bpAddress = resMember.token_addr;
if (bpAddress === undefined || !bpAddress) {
console.log('Returned bpAddress Invalid:', resMember);
window.location.href = environment.portal;
return false;
}
...
);

// https://github.com/boraecosystem/game-front/blob/master/src/app/api.service.ts#L48

public getMembers(userAccessToken: string): Observable {
const headers = new HttpHeaders({
'Authorization': 'Bearer ' + userAccessToken,
});
return this.http.get(environment.api + 'chain/v1/services/members', { headers: headers })
.map(res => res);
}
이 부분까지 사용자에게 BORA Shell을 지급하기 전 사용자의 주소를 확인하는 단계를 설명 했습니다.본 예제 코드에서는 주소를 얻기 위해 매번 인증 과정을 거치도록 구현 되어 있으나, 이 과정은 컨텐츠 제공자의 서비스에서 회원가입 또는 BORA 플랫폼과 연동하는 적당한 단계에서 수행하여, database 등에 저장하여 사용하는 방법을 고려해 보십시오.다음은 점수를 얻을 때마나 BORA Shell을 유저에게 지급해 보도록 하겠습니다.

포인트를 유저에게 지급하기

본 게임에서는 점수를 획득할 때마다 컨텐츠 제공자가 소유한 BORA Shell을 유저에게 전달하며, 컨텐츠 제공자의 인증 코드를 이용하여 API 호출을 통해 Point를 유저에게 전달합니다.
  • OAuth 2.0 Client Credential Grant 방식으로 컨텐츠 제공자의 access token을 얻음
  • access token을 이용하여 포인트 지급 API를 호출하여 포인트를 유저에게 전송함

컨텐츠 제공자의 Access Token 발급

컨텐츠 제공자에게 사전 발급한 client Id, client secret를 이용하여 access token을 발급 받습니다. client id, client secret을 사전 발급 받는 기능은 추후 제공 될 예정이며, 여기에서는 테스트 목적으로 사전 발급한 client id와 client secret을 사용합니다.
// clientId: X0yT6nI69Q, clientSecret: yM5pnll9GwdMjkapd7MWX0
// https://github.com/boraecosystem/game-pacman/blob/master/app.js#L141

const httpOAuth = require('http');
function getClientToken(cb) {
const data = 'grant_type=client_credentials';
const req = httpOAuth.request({
protocol: 'https:',
host: chain_api_uri,
port: 443,
method: 'post',
path: '/member/oauth/token',
auth: `${clientId}:${clientSecret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}, res => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => cb(JSON.parse(body)));
}
);
req.on('error', e => console.error(`problem with request: ${e.message}`));
req.write(data);
req.end();
}

사용자에게 BORA Shell 지급

점수를 획득한 사용자에게 BORA Shell 을 Chain API를 호출하여 지급합니다.
// https://github.com/boraecosystem/game-pacman/blob/master/app.js#L69

// In Game Server

const chainApi = require('./module/chainApi')('dev');
app.get("/saveScore", function (req, res) {
var amount = req.query.amount;
const toAddr = req.cookies.bpAddress;
const apiuri = chain_api_uri;
chainApi.saveScore(apiuri, clientAccessToken, toAddr, amount, function (error, data) {
if (!error) {
res.send(data);
} else {
res.send(data);
}
});
});
// Request Chain-API
// https://github.com/boraecosystem/game-pacman/blob/master/module/chainApi.js#L12

saveScore: function (host, clientAccessToken, toAddr, amount, callback) {
OPTIONS.url = 'https://' + host + '/chain/v1/services/deposit/' + toAddr + '/' + amount;
OPTIONS.body = '';
OPTIONS.headers = { Authorization: 'Bearer ' + clientAccessToken };
request.get(OPTIONS, function (err, res, result) {
statusCodeErrorHandler(res.statusCode, callback, result);
});
여기까지, 필수 API 사용 방법에 대해 안내해 드렸습니다. 다음은 BORA Shell이 지급된 이력을 확인하기 위한 방법입니다. 여기에서는 BORA Explore API를 이용한 방법을 설명했으나, 향후 이력 확인을 위한 Chain API를 제공할 예정입니다.

포인트 획득 이력을 확인함

본 영역은, 게임 구성의 필수 요소가 아니며 블록체인에 트랜잭션이 기록 되는 과정을 노출하기 위함입니다. 사용한 API는 BORA Explorer에서 제공하는 API 입니다.
// https://github.com/boraecosystem/game-front/blob/master/src/app/api.service.ts#L13

import { HttpClient, HttpHeaders } from '@angular/common/http';
const http: HttpClient;

public bp_tx_list_by_addr(appId: number, address: string, page: number = 1, page_size: number = 1): Observable {
return this.http.get(environment.bpapi + 'points/' + appId + '/addresses/' + address + '/txs?page=' + page + '&pageSize=' + page_size)
.map(res => res);
}
public bp_block_list(appId: number, page: number = 1, page_size: number = 10): Observable {
return this.http.get(environment.bpapi + 'points/' + appId + '/blocks?page=' + page + '&pageSize=' + page_size)
.map(res => res);
}
public bp_info_addr(appId: number, address: string): Observable {
return this.http.get(environment.bpapi + 'points/' + appId + '/addresses/' + address)
.map(res => res);
}

참고 API 문서

API 별 상세한 사용 방법은 아래 API 문서를 참고하시기 바랍니다.Authorization APIsChain APIsBORA Explorer APIs