WEB3DEV

Cover image for Como Desenvolver um Jogo de Blockchain sem Carteira com o GraphQL
Panegali
Panegali

Posted on

Como Desenvolver um Jogo de Blockchain sem Carteira com o GraphQL

O GraphQL é um tipo de biblioteca Web Socket. Você precisa definir o servidor GraphQL no lado do servidor, mas isso é muito útil para notificar facilmente os jogadores. O próprio GraphQL é muito usado no aplicativo do Twitter e dos principais aplicativos.

Eu disse que você precisa configurar o servidor GraphQL, mas também pode usar algumas ferramentas da web.

O GraphQL e o onflow/fcl são bibliotecas modernas e têm muitas semelhanças. Ambas usam linguagens originais, gql (linguagem GraphQL) e Cadence. Quando você quiser chamar essas funções, você passará essas funções da linguagem original para gql no GraphQL e no Cadence, e definirá métodos dedicados e também passará variáveis que são usadas nessas funções.

Quando você imagina o jogos de blockchain, que tipo de coisa você imagina?

Você pode pensar qual é o benefício de usar a blockchain.

Nos apps (aplicativos) de blockchain, você fará as transações. No sentido de transação, trata-se de transferência de dinheiro. Portanto, você pode enviar e receber dinheiro enquanto joga os jogos de blockchain. Mas a transação na blockchain em si não é usada apenas para transferir dinheiro, ela pode salvar dados de objetos dentro da blockchain. Portanto, você pode usar transações para salvar dados do jogo. Mas o objetivo principal é enviar dinheiro e NFTs.

Porque isso é importante é que, quando você deseja jogar um jogo de alta rede (high-networking), o jogo está, na verdade, sendo executado em PCs comuns dentro do GameArcade no Japão, embora os gabinetes sejam diferentes dos PCs comuns, mas está sendo executado no sistema operacional Windows. Portanto, se você pode inserir moedas em sua casa, pode jogar Arcade Games em sua casa. O principal objetivo das transações de jogos de blockchain é enviar dinheiro digital para jogos de alta rede. Não é mais necessário comprar consoles de jogos, porque o jogo em si é baixado da Internet.

Mas você pode estar se perguntar que, se o próprio jogo sofrer engenharia reversa e for usado como software pirata, os fornecedores do jogo sofrerão perdas. Portanto, o próprio jogo em si deve ser protegido pelo console dos jogos ou algo assim. Mas isso provavelmente não acontece nos jogos de blockchain. Não apenas os jogadores pagam e transferem dinheiro para os provedores de jogos, mas os provedores de jogos também podem enviar e transferir dinheiro digital para os jogadores. Como os endereços das carteiras são distintos, sempre que os provedores de jogos quiserem enviar dinheiro digital, eles poderão fazê-lo. E a taxa de transação do Fluxo é quase 0, portanto, não há nada a perder durante as transações.

Assim, quando você puder receber os prêmios ou NFTs de destaque dos provedores de jogos ao vencer o jogo, nunca jogará jogos de software pirata.

O que é o jogo de blockchain sem carteira?

Eu disse anteriormente que as transações de blockchain não são usadas apenas para transferir moeda digital, mas também para salvar dados do jogo. Mas as transações levarão alguns segundos (cerca de 5s), e os jogadores não querem esperar tanto tempo se as transações forem usadas apenas para salvar os dados do jogo. Pense nisso, se toda vez que o pop-up da carteira aparecer durante o jogo, você perderá o ritmo. Portanto, o ideal é que o pop-up da carteira seja usado apenas para obter dinheiro, pagar moeda digital e conectar e importar dados de recursos do jogo.

Portanto, esse jogo de blockchain sem carteira é importante. E se você usou o GraphQL, também pode usar (quero dizer, implementar) as funções de rede social, pois o GraphQL é bom em redes WebSocket.

É difícil de implementar?

Não, de forma alguma. Como eu disse, há muitas semelhanças entre o GraphQL e o onflow/fcl, você pode implementar funções sem carteira muito rapidamente, então escrevo como fazer isso neste artigo.

Antes de implementar...

Se você não conhece os tipos Consulta (Query), Mutação (Mutation) e Entrada (Input), antes de ler o restante desta página, recomendo que leia a página oficial para que possa entender tanto o fcl quanto o GraphQL.

https://graphql.org/learn/schema/

Agora que você entende o GraphQL e o fcl, vamos começar! Como a linguagem GraphQL é bastante difícil de aprender, existem muitas ferramentas de criação automática. Depois de digitar o esquema GraphQL abaixo.

type BCGGameServerProcess @model {
id: ID!
type: String!
message: String!
playerId: String!
}

type Subscription {
onCreateByPlayerid(playerId: String!): BCGGameServerProcess
@aws_subscribe(mutations: ["createBCGGameServerProcess"])
}
Enter fullscreen mode Exit fullscreen mode

As principais ferramentas do GraphQL têm funções de criação automática de funções de consulta, mutation e assinatura.

Neste caso, estou usando o AppSync.

O GraphQL tem oficialmente algumas diretivas como @include ou @skip. (Mais detalhes estão no link abaixo.)

https://graphql.org/learn/queries/#directives

Mas o AppSync preparou para você a diretiva @model. E depois de digitar o comando amplify push no terminal, você obterá todos os métodos de consulta, mutação, assinatura e também as funções do servidor GraphQL, se colocar a diretiva @model no seu tipo.

Se você quiser saber como fazer isso, compre o livro abaixo. Neste livro a instrução é explicada com mais de 30 fotos.

https://www.amazon.com/dp/B0BVCY7M4R/

Agora que você tem todos os requisitos do método.

Isso inclui

Funções de consulta get/list em /src/graphql/queries.js.

export const getBCGGameServerProcess = /* GraphQL */ `
query GetBCGGameServerProcess($id: ID!) {
getBCGGameServerProcess(id: $id) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
export const listBCGGameServerProcesses = /* GraphQL */ `
query ListBCGGameServerProcesses(
$filter: ModelBCGGameServerProcessFilterInput
$limit: Int
$nextToken: String
) {
listBCGGameServerProcesses(
filter: $filter
limit: $limit
nextToken: $nextToken
) {
items {
id
type
message
playerId
createdAt
updatedAt
}
nextToken
}
}
`;
Enter fullscreen mode Exit fullscreen mode

Funções de criação, atualização e exclusão de mutação em /src/graphql/mutations.js.

export const createBCGGameServerProcess = /* GraphQL */ `
mutation CreateBCGGameServerProcess(
$input: CreateBCGGameServerProcessInput!
$condition: ModelBCGGameServerProcessConditionInput
) {
createBCGGameServerProcess(input: $input, condition: $condition) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
export const updateBCGGameServerProcess = /* GraphQL */ `
mutation UpdateBCGGameServerProcess(
$input: UpdateBCGGameServerProcessInput!
$condition: ModelBCGGameServerProcessConditionInput
) {
updateBCGGameServerProcess(input: $input, condition: $condition) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
export const deleteBCGGameServerProcess = /* GraphQL */ `
mutation DeleteBCGGameServerProcess(
$input: DeleteBCGGameServerProcessInput!
$condition: ModelBCGGameServerProcessConditionInput
) {
deleteBCGGameServerProcess(input: $input, condition: $condition) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
Enter fullscreen mode Exit fullscreen mode

Funções de criação, atualização e exclusão de assinatura em /src/graphql/subscriptions.js.

export const onCreateBCGGameServerProcess = /* GraphQL */ `
subscription OnCreateBCGGameServerProcess(
$filter: ModelSubscriptionBCGGameServerProcessFilterInput
) {
onCreateBCGGameServerProcess(filter: $filter) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
export const onUpdateBCGGameServerProcess = /* GraphQL */ `
subscription OnUpdateBCGGameServerProcess(
$filter: ModelSubscriptionBCGGameServerProcessFilterInput
) {
onUpdateBCGGameServerProcess(filter: $filter) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
export const onDeleteBCGGameServerProcess = /* GraphQL */ `
subscription OnDeleteBCGGameServerProcess(
$filter: ModelSubscriptionBCGGameServerProcessFilterInput
) {
onDeleteBCGGameServerProcess(filter: $filter) {
id
type
message
playerId
createdAt
updatedAt
}
}
`;
Enter fullscreen mode Exit fullscreen mode

Depois disso, basta importar essas funções para dentro do seu arquivo javascript de front-end e chamá-las para que o resolvedor do servidor GraphQL será chamado.

Você pode verificar a aparência do resolvedor na página oficial.

https://graphql.org/learn/execution/#root-fields-resolvers

E quero observar porque coloquei isto (↓) abaixo do tipo é porque a assinatura costuma ser personalizada para enviar a cada jogador do jogo, de modo que geralmente as implementações do método de assinatura são necessárias.

type Subscription {
onCreateByPlayerid(playerId: String!): BCGGameServerProcess
@aws_subscribe(mutations: ["createBCGGameServerProcess"])
}
Enter fullscreen mode Exit fullscreen mode

Agora, sua implementação de front-end sem carteira está concluída e está assim:

async putCardOnTheField(field_position, card_id, used_intercept_card, enemy_skill_target) {
this.customLoading = true
setTimeout(() => (this.customLoading = false), 5000)
console.log("DEBUG The Card Put on the Field:", this.your_trigger_cards, enemy_skill_target, used_intercept_card)
// this.loadingDialog = true
console.log("LOG: Call a GraphQL mutation method to run Direct Lambda Resolver function (which located in serverside).")
const message = {
arg1: [{key: field_position, value: card_id }],
arg2: enemy_skill_target || 0,
arg3: [
{key: 1, value: this.your_trigger_cards[1] || 0},
{key: 2, value: this.your_trigger_cards[2] || 0},
{key: 3, value: this.your_trigger_cards[4] || 0},
{key: 4, value: this.your_trigger_cards[4] || 0},
],
arg4: used_intercept_card
}
const callProcess = {
type: 'put_card_on_the_field',
message: JSON.stringify(message),
playerId: this.player_id
}
await API.graphql({
query: createBCGGameServerProcess,
variables: { input: callProcess },
}).then((res) => {
console.log('LOG: GraphQL', res)
}).catch((err) => {
console.log('Error:', err)
})
// const transactionId = await this.$fcl.mutate({
// cadence: FlowTransactions.putCardOnField,
// args: (arg, t) => [
// arg([{key: field_position, value: card_id }], t.Dictionary({ key: t.UInt8, value: t.UInt16 })), // unit_card
// arg(enemy_skill_target || 0, t.UInt8), // enemy_skill_target
// arg([
// {key: 1, value: this.your_trigger_cards[1] || 0},
// {key: 2, value: this.your_trigger_cards[2] || 0},
// {key: 3, value: this.your_trigger_cards[4] || 0},
// {key: 4, value: this.your_trigger_cards[4] || 0},
// ], t.Dictionary({ key: t.UInt8, value: t.UInt16 })), // trigger_cards
// arg(used_intercept_card, t.Array(t.UInt8)) // used_intercept_card_positions
// ],
// proposer: this.$fcl.authz,
// payer: this.$fcl.authz,
// authorizations: [this.$fcl.authz],
// limit: 999
// })
// console.log(`TransactionId: ${transactionId}`)
this.show_game_dialog = false
this.turnChangeActionDone = true
this.checkTransactionComplete('putCardOnTheField')
},
Enter fullscreen mode Exit fullscreen mode

O tipo GraphQL tem os campos message, playerId e type, de modo que, em vez de chamar o método onflow/fcl, basta substituí-lo pela consulta GraphQL createBCGGameServerProcess por essas variáveis.

Em seguida, implementação do lado do servidor

No lado do servidor, o servidor GraphQL chamará a função de resolução, que está descrita abaixo na página oficial.

Query: {
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
Enter fullscreen mode Exit fullscreen mode

https://graphql.org/learn/execution/#root-fields-resolvers

Você pode pensar que é um pouco complicado, mas o AppSync tem o recurso Direct Lambda Resolver.

Você pode definir apenas a chamada de uma função Lambda para responder às chamadas da função GraphQL e isso é realmente muito eficaz e simples.

1. Crie uma função Lambda

Crie uma função Lambda do zero.

2. Escreva um código de amostra

Escreva valores de retorno que sejam iguais aos campos de tipo. Ele também precisa de id, createdAt e updatedAt se a chamada do GraphQL for a função de criação de mutação. E pressione o botão Deploy (Implantar).

3. Abra o console do AppSync e escolha a API atual

A API atual foi criada pelo comando amplify push.

4. No painel esquerdo, pressione Data Sources e pressione o botão Create data source (Criar fonte de dados).

**

5. Copie o nome da função Lambda e cole na parte superior e selecione AWS Lambda Function como tipo de fonte de dados.

Selecione a mesma região em que a função Lambda foi implantada.

Selecione Lambda em Function ARN. A função é a mesma e pressione o botão Create (Criar).

Em seguida, pressione a guia Schema no painel esquerdo. E digite “Mutation” dentro da barra de pesquisa.

6. Anexe a fonte de dados ao resolvedor do GraphQL

Primeiro, o modo de resolução de pipeline não é adequado para o recurso Lambda, portanto, pressione o botão Actions (Ações) e selecione Update runtime (Atualizar tempo de execução).

Selecione Unit Resolver (Resolvedor de unidades) e pressione o botão Update (Atualizar)

Escolha a fonte de dados.

E, finalmente, pressione Save resolver (Salvar o resolvedor).

Agora, seu resolvedor tem a seguinte aparência.

Este é processo completo que você precisa fazer para preparar o servidor GraphQL.

Você acha que isso é difícil? Eu acredito que você não pensará isso.

OK, agora a última coisa é apenas escrever as transações dentro deste resolvedor Lambda. Mas, antes de escrever códigos de transação na função Lambda, é necessária alguma dependência, como @onflow/fcl, então baixe o código Lambda no seu computador e acesse a mesma pasta com o terminal e execute o comando npm install para criar a pasta node_modules. E depois que seu código estiver pronto, digite abaixo para compactar a pasta inteira. A última coisa é fazer o upload para o console do Lambda.

zip -r ../src.zip *
Enter fullscreen mode Exit fullscreen mode

E no meu caso, a função do resolvedor é parecida com esta:

const fs = require('fs');
const fcl = require("@onflow/fcl");
const t = require("@onflow/types");
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
const { SHA3 } = require("sha3");

const FlowTransactions = {
matchingStart: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.matching_start(player_id: player_id)
}
execute {
log("success")
}
}
`,
gameStart: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32, drawed_cards: [UInt16]) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.game_start(player_id: player_id, drawed_cards: drawed_cards)
}
execute {
log("success")
}
}
`,
putCardOnField: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32, unit_card: {UInt8: UInt16}, enemy_skill_target: UInt8?, trigger_cards: {UInt8: UInt16}, used_intercept_positions: [UInt8]) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.put_card_on_the_field(player_id: player_id, unit_card: unit_card, enemy_skill_target: enemy_skill_target, trigger_cards: trigger_cards, used_intercept_positions: used_intercept_positions)
}
execute {
log("success")
}
}
`,
turnChange: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32, attacking_cards: [UInt8], enemy_skill_target: {UInt8: UInt8}, trigger_cards: {UInt8: UInt16}, used_intercept_position: {UInt8: [UInt8]}) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.turn_change(player_id: player_id, attacking_cards: attacking_cards, enemy_skill_target: enemy_skill_target, trigger_cards: trigger_cards, used_intercept_position: used_intercept_position)
}
execute {
log("success")
}
}
`,
startYourTurn: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32, blocked_unit: {UInt8: UInt8}, used_intercept_position: {UInt8: UInt8}) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.start_your_turn_and_draw_two_cards(player_id: player_id, blocked_unit: blocked_unit, used_intercept_position: used_intercept_position)
}
execute {
log("success")
}
}
`,
surrendar: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.surrendar(player_id: player_id)
}
execute {
log("success")
}
}
`,
claimWin: `
import CodeOfFlowAlpha6 from 0x9e447fb949c3f1b6
transaction(player_id: UInt32) {
prepare(signer: AuthAccount) {
let admin = signer.borrow<&CodeOfFlowAlpha6.Admin>(from: CodeOfFlowAlpha6.AdminStoragePath)
?? panic("Could not borrow reference to the Administrator Resource.")
admin.claimWin(player_id: player_id)
}
execute {
log("success")
}
}
`,
}

exports.handler = async function (event) {
console.log("Event", JSON.stringify(event, 3))
const input = event.arguments?.input || {};

let player_id = input.playerId ? parseInt(input.playerId) : 0
let message = input.message ? JSON.parse(input.message) : {}
var KEY_ID_IT = 1
if (fs.existsSync('/tmp/sequence.txt')) {
KEY_ID_IT = parseInt(fs.readFileSync('/tmp/sequence.txt', {encoding: 'utf8'}));
}
try {
const client = new SecretsManagerClient({region: "ap-northeast-1"});
const response = await client.send(new GetSecretValueCommand({
SecretId: "SmartContractPK",
VersionStage: "AWSCURRENT",
}));

const EC = require('elliptic').ec;

const ec = new EC('p256');
fcl.config()
.put("accessNode.api", "https://rest-testnet.onflow.org")

// MUDAR ISSO PARA VOCÊ
const PRIVATE_KEY = JSON.parse(response.SecretString)?.SmartContractPK;
const ADDRESS = "0x9e447fb949c3f1b6";
const KEY_ID = 0;
const CONTRACT_NAME = "CodeOfFlowAlpha6";

const sign = (message) => {
const key = ec.keyFromPrivate(Buffer.from(PRIVATE_KEY, "hex"))
const sig = key.sign(hash(message)) // hashMsgHex -> hash
const n = 32
const r = sig.r.toArrayLike(Buffer, "be", n)
const s = sig.s.toArrayLike(Buffer, "be", n)
return Buffer.concat([r, s]).toString("hex")
}
const hash = (message) => {
const sha = new SHA3(256);
sha.update(Buffer.from(message, "hex"));
return sha.digest();
}

async function authorizationFunction(account) {
return {
...account,
tempId: `${ADDRESS}-${KEY_ID}`,
addr: fcl.sansPrefix(ADDRESS),
keyId: Number(KEY_ID),
signingFunction: async (signable) => {
return {
addr: fcl.withPrefix(ADDRESS),
keyId: Number(KEY_ID),
signature: sign(signable.message)
}
}
}
}
async function authorizationFunctionProposer(account) {
KEY_ID_IT = !KEY_ID_IT || KEY_ID_IT > 5 ? 1 : KEY_ID_IT + 1
fs.writeFileSync('/tmp/sequence.txt', KEY_ID_IT.toString());
return {
...account,
tempId: `${ADDRESS}-${KEY_ID_IT}`,
addr: fcl.sansPrefix(ADDRESS),
keyId: Number(KEY_ID_IT),
signingFunction: async (signable) => {
return {
addr: fcl.withPrefix(ADDRESS),
keyId: Number(KEY_ID_IT),
signature: sign(signable.message)
}
}
}
}
if (input.type === "player_matching") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.matchingStart,
args: (arg, t) => [
arg(player_id, t.UInt32)
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "game_start") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.gameStart,
args: (arg, t) => [
arg(player_id, t.UInt32),
arg(message, t.Array(t.UInt16))
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "put_card_on_the_field") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.putCardOnField,
args: (arg, t) => [
arg(player_id, t.UInt32),
arg(message.arg1, t.Dictionary({ key: t.UInt8, value: t.UInt16 })), // unit_card
arg(message.arg2, t.UInt8), // enemy_skill_target
arg(message.arg3, t.Dictionary({ key: t.UInt8, value: t.UInt16 })), // trigger_cards
arg(message.arg4, t.Array(t.UInt8)) // used_intercept_positions
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "turn_change") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.turnChange,
args: (arg, t) => [
arg(player_id, t.UInt32),
arg(message.arg1, t.Array(t.UInt8)), // attacking_cards
arg(message.arg2, t.Dictionary({ key: t.UInt8, value: t.UInt8 })), // enemy_skill_target
arg(message.arg3, t.Dictionary({ key: t.UInt8, value: t.UInt16 })), // trigger_cards
arg(message.arg4, t.Dictionary({ key: t.UInt8, value: t.Array(t.UInt8) })) // used_intercept_position
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "start_your_turn") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.startYourTurn,
args: (arg, t) => [
arg(player_id, t.UInt32),
arg(message.arg1, t.Dictionary({ key: t.UInt8, value: t.UInt8 })), // blocked_unit
arg(message.arg2, t.Dictionary({ key: t.UInt8, value: t.UInt8 })), // used_intercept_position
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "surrendar") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.surrendar,
args: (arg, t) => [
arg(player_id, t.UInt32),
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
} else if (input.type === "claim_win") {
const transactionId = await fcl.mutate({
cadence: FlowTransactions.claimWin,
args: (arg, t) => [
arg(player_id, t.UInt32),
],
proposer: authorizationFunctionProposer,
payer: authorizationFunction,
authorizations: [authorizationFunction],
limit: 999
})
console.log(`TransactionId: ${transactionId}`)
message = `Transaction is On Going. TransactionId: ${transactionId}`
fcl.tx(transactionId).subscribe(res => {
console.log(res);
})
}

return {
id: new Date().getTime(),
type: input.type || "",
message: KEY_ID_IT + " : " + message,
playerId: player_id,
createdAt: new Date(),
updatedAt: new Date()
};
} catch (error) {
return {
id: new Date().getTime(),
type: input.type || "",
message: error.toString(),
playerId: player_id,
createdAt: new Date(),
updatedAt: new Date()
};
}
};
Enter fullscreen mode Exit fullscreen mode

E a codificação do lado do servidor para o GraphQL está concluída.

Vamos testar

Agora, você não precisa pressionar o botão Approve no pop-up da carteira.

E mesmo que você não saiba como a assinatura funciona dentro do AppSync, ela (=GraphQL Subscription) realmente funciona. Portanto, você pode criar qualquer função de comunicação de rede. Informando ao jogador adversário que a transação está em andamento no mometo, mesmo que você não tenha configurado isso para o resolvedor.


API.graphql({
query: onCreateBCGGameServerProcess,
}).subscribe({
next: (serverData) => {
console.log('BCGGameServerProcess SubscriptionData:', serverData)
},
})
Enter fullscreen mode Exit fullscreen mode

E este é o fim do artigo. Muito obrigado por ler isso.


Artigo escrito por Takashi Tahara. Traduzido por Marcelo Panegali.

Top comments (0)