V-ENT para estúdios de jogos
Realize torneios V-ENT dentro do seu jogo
Ligue o seu jogo uma vez e todos os formatos V-ENT funcionam nele, os de hoje e os que ainda vêm. O seu jogo só trata de uma sessão de jogo de cada vez; a V-ENT trata do quadro, das tabelas, do dinheiro e da transmissão à volta.
Ligar um jogoComo tudo se encaixa
- Ligue o seu jogo na secção Estúdios do portal de parceiros. Começa no ambiente de testes, com um segredo de assinatura mostrado uma única vez.
- Os jogadores associam a conta de jogo à conta V-ENT, com um código que o seu jogo mostra ou com o início de sessão com a V-ENT.
- Um organizador liga o torneio ao seu jogo a partir da consola do torneio.
- Quando uma partida está pronta, a V-ENT pede uma sala ao seu jogo. O seu jogo responde com um identificador de sessão e a forma de entrar.
- Quando o jogo termina, o seu servidor envia o resultado em bruto. A V-ENT verifica-o, pontua-o com a sua expressão e regista-o na partida.
- Peça a entrada em produção quando tudo funcionar. Um administrador da V-ENT revê o jogo primeiro.
O ambiente de testes e as chaves de teste
Um jogo no ambiente de testes só chega aos torneios que a sua própria conta organiza e aos testadores que indicar, para construir sobre quadros reais sem tocar nos de mais ninguém. Crie uma chave de teste no portal: começa pelo prefixo abaixo e só chega aos jogos no ambiente de testes. Uma chave de produção só chega aos jogos em produção.
Authorization: Bearer vent_pk_test_<key id>.<secret>Cada pedido é assinado, nos dois sentidos
Cada pedido que a V-ENT lhe envia, e cada atualização que nos envia, é assinado com HMAC SHA-256 com o segredo de assinatura do seu jogo, sobre o método, o caminho e a consulta, uma marca temporal, o anfitrião e uma impressão do corpo. Recuse tudo o que for mais antigo do que a janela abaixo e guarde cada identificador de pedido visto durante esse tempo, para que o mesmo pedido não possa ser repetido.
{METHOD}\n{path and query}\n{timestamp};{host};{base64 SHA-256 of the body}V-Ent-Timestamp- O momento da assinatura, em segundos unix
V-Ent-Content-SHA256- O SHA-256 do corpo, em base64
V-Ent-Signature- v1= seguido da assinatura, uma vez por cada segredo válido
V-Ent-Delivery- Um identificador de pedido usado uma vez; guarde-o durante a janela para recusar repetições
- Janela
- 300 segundos antes ou depois de agora
Depois de renovar o segredo, o antigo continua válido durante 24 horas e cada pedido leva uma assinatura v1= por segredo, para trocar sem perder nada.
import base64, hashlib, hmac, time
def verify(secret, method, path_and_query, host, headers, body):
stamp = int(headers["V-Ent-Timestamp"])
if abs(time.time() - stamp) > 300:
raise ValueError("stale")
content = base64.b64encode(hashlib.sha256(body).digest()).decode()
if not hmac.compare_digest(content, headers["V-Ent-Content-SHA256"]):
raise ValueError("body changed")
text = f"{method}\n{path_and_query}\n{stamp};{host};{content}"
expected = base64.b64encode(
hmac.new(secret.encode(), text.encode(), hashlib.sha256).digest()).decode()
offered = [p.strip()[3:] for p in headers["V-Ent-Signature"].split(",")]
if not any(hmac.compare_digest(expected, o) for o in offered):
raise ValueError("bad signature")import crypto from 'node:crypto';
export function verify(secret, method, pathAndQuery, host, headers, body) {
const stamp = Number(headers['v-ent-timestamp']);
if (Math.abs(Date.now() / 1000 - stamp) > 300) throw new Error('stale');
const content = crypto.createHash('sha256').update(body).digest('base64');
if (content !== headers['v-ent-content-sha256']) throw new Error('body changed');
const text = `${method}\n${pathAndQuery}\n${stamp};${host};${content}`;
const expected = crypto.createHmac('sha256', secret).update(text).digest('base64');
const offered = headers['v-ent-signature'].split(',').map(p => p.trim().slice(3));
const ok = offered.some(o => o.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(o), Buffer.from(expected)));
if (!ok) throw new Error('bad signature');
}O que a V-ENT lhe envia
Cada pedido tem o mesmo corpo, por isso um único endereço pode recebê-los todos. As chamadas de sessão de jogo vão para o seu endereço de sessões; os eventos vão para o seu endereço de eventos, ou para o de sessões se o deixou vazio.
{
"id": "dl_8f2c...",
"type": "event",
"event": "result_confirmed",
"created_at": "2026-10-17T18:04:11Z",
"game": "<your game>",
"environment": "live",
"data": { }
}eventEventocreate_game_sessionCriar uma sessão de jogocancel_game_sessionCancelar uma sessão de jogoget_game_sessionPedir uma sessão de jogo
Responda com qualquer 2xx e o pedido fica entregue. Qualquer outra resposta, ou nenhuma em 10 segundos, é repetida segundo o calendário abaixo e depois abandonada. Responda 410 para parar de imediato. Cada pedido e o que o seu servidor respondeu estão no registo de envios na página do seu jogo, e por chave em /api/v1/studio/games/<game>/deliveries/.
Esperas entre tentativas para um evento, em segundos: 30, 120, 600, 1.800, 3.600, 10.800, 21.600, 43.200, 86.400
Sessões de jogo
Uma melhor de três tem até três sessões de jogo, uma por jogo. Quando uma partida está pronta enviamos create_game_session com as duas formações (com a conta de jogo de cada jogador que a associou), as definições escolhidas pelo organizador e uma chave de idempotência. Trate uma chave repetida como a mesma sala: voltamos a chamar até responder created ou mais adiante.
{ "session_id": "your-lobby-42", "state": "created",
"join": { "room_code": "ROOM42", "room_password": "pw" } }Um room_code em join é mostrado aos dois lados na sala da partida, a não ser que tenham escrito um deles.
Os estados só avançam: creating, created, starting, started, finished, ou cancelled. Envie cada alteração, assinada, para o endereço de retorno dado pela chamada de criação. Um jogo que falha é cancelado sem resultado e a V-ENT abre a tentativa seguinte do mesmo jogo, até três.
curl -X POST https://api.v-ent.co/api/v1/studio/sessions/gs_<ref>/ \
-H "Authorization: Bearer vent_pk_<key id>.<secret>" \
-H "V-Ent-Timestamp: <unix seconds>" \
-H "V-Ent-Content-SHA256: <base64 sha256 of the body>" \
-H "V-Ent-Signature: v1=<signature>" \
-H "V-Ent-Delivery: <a new id for every request>" \
-d '{"state": "finished", "result": { }}'Se um jogo começado ficar em silêncio durante cinco minutos perguntamos com get_game_session; responda com o mesmo corpo que enviaria. Quando uma partida é decidida de outra forma enviamos cancel_game_session. Nunca cancelamos um jogo terminado.
Resultados e pontuação
Um resultado tem duas formações, uma para o lado 1 e outra para o lado 2, uma lista opcional de competidores (cada um com um lado) e um objeto opcional para o jogo inteiro. Lá dentro, os campos são seus, verificados com o esquema de resultado do seu jogo.
{
"lineups": [ { "side": 1, "score": 13, "won": true },
{ "side": 2, "score": 7, "won": false } ],
"competitors": [ { "account_id": "player-one", "side": 1, "kills": 9 } ],
"general": { "map": "Bermuda" }
}Palavras-chave de esquema que a V-ENT verifica: type, properties, required, items, enum, minimum, maximum, minLength, maxLength, minItems, maxItems, additionalProperties, description, title, $schema. Um esquema que use outra palavra-chave é recusado ao guardar, em vez de ser verificado a meias.
Expressões de pontuação
A sua expressão transforma o resultado em bruto de um lado na sua pontuação nesse jogo. É calculada uma vez por lado e a resposta tem de ser um número inteiro de 0 a 999.
team.score
competitors.sum(it.kills)
team.roundsWon + competitors.sum(it.kills)
global.double ? team.roundsWon * 2 : team.roundsWon
settings.mode == 'KillRace' ? competitors.sum(it.kills) : (team.won ? 1 : 0)- Nomes
team, opponent, competitors, global, settings- Funções
abs, ceil, floor, max, min, round- Numa lista
.all(it...), .any(it...), .avg(it...), .count(it...), .max(it...), .min(it...), .sum(it...)
A expressão é lida pelo pequeno analisador da V-ENT e nunca corre como código. Experimente-a com um resultado de exemplo na página do seu jogo antes de a guardar.
O resultado chega à partida pelas mesmas regras que um resultado escrito à mão. Tudo o que essas regras recusam, como um empate a eliminar, espera pelo organizador, que tem a última palavra.
Associar as contas dos jogadores
Com um código: o seu servidor pede um para a sua conta, o seu jogo mostra-o e o jogador escreve-o em v-ent.co/link-game com sessão iniciada. Vê o seu jogo e o nome da conta antes de confirmar. Um código dura dez minutos e só serve uma vez. Pergunte se foi usado pelo code_ref, nunca pelo código.
curl -X POST https://api.v-ent.co/api/v1/studio/games/<game>/link-codes/ \
-H "Authorization: Bearer vent_pk_<key id>.<secret>" \
-d '{"account_id": "your-account-id", "display_name": "Ada#1"}'Com o início de sessão com a V-ENT: inicie a sessão do jogador a partir do seu jogo e envie o token de acesso com o seu identificador de conta para /api/v1/studio/games/<game>/links/. O token tem de ser seu e ter o âmbito identity.
Eventos
A mesma lista que as sobreposições e o alinhamento da V-ENT leem. Escolha quais receber na página do seu jogo; não escolha nenhum para receber todos. Um evento de teste pode ser enviado a partir da página a qualquer momento.
result_confirmed- Foi registado o resultado de uma partida
stage_started- Uma fase começou
stage_closed- Uma fase terminou
line_sent- Foram enviados lugares para outra fase
tournament_won- Um torneio foi ganho
account_linked- Um jogador associou a sua conta
account_unlinked- A associação de um jogador foi removida
Ler para os seus próprios ecrãs
Mostre um torneio dentro do seu jogo com a API de leitura: as fases e as suas classificações (a mesma classificação que o site mostra) e a próxima partida de um jogador associado. Cada resposta diz quanto resta do seu limite de pedidos, para abrandar antes de ser recusado.
GET /api/v1/tournaments/<slug>/structure/
GET /api/v1/tournaments/<slug>/standings/
GET /api/v1/studio/games/<game>/accounts/<account_id>/next-match/
X-Rate-Limit-Limit: 60
X-Rate-Limit-Remaining: 59
X-Rate-Limit-Reset: 42Desde a primeira sessão, a página do seu jogo conta os jogadores associados que jogaram em cada mês e as sessões jogadas. Hoje nada é cobrado por isso.