Pular para o conteúdo principal
Serviços em torno do jogo

Esta página ainda não foi traduzida: mostrando a versão em inglês.

Inventory

Um único conjunto de linhas com dono, usado por tiros, drops, habilidades e movimentação. As pilhas mudam por incremento, e o teto por dono se comporta no limite como você escolher.

Quando usar

  • Os jogadores possuem coisas, e cada posse é uma linha com dono: lida por dono, com teto por dono e com o comportamento de excedente declarado, não herdado de um padrão.
  • Uma quantidade se acumula: uma pilha (stack) muda por incremento com chave de idempotência, então um débito repetido não debita duas vezes.
  • Outros módulos gastam do mesmo conjunto: tiros debitam munição, drops concedem loot, compras aparecem como linhas vinculadas ao direito de uso (entitlement) correspondente.
  • Dispensável quando o número não é algo que se possua. HP, XP e cooldowns ficam nos stats.

Quem faz o quê

AtorO que faz nesta página
playerlê as próprias posses e gasta delas
backend-serviceconcede, incrementa e revoga em nome de um jogador, informando para quem está agindo

Resumo

Com autoridade fn: conceder munição, mover um item para o slot de equipamento primary, verificar se o saldo basta antes de gastar
// fn authority — a cloud function, or a dedicated server holding a host key
var bag = await player.Inventory.Container("bag");
var equipment = await player.Inventory.Container("equipment");

await player.Inventory.Grant("ammo.shell", count: 20);
await bag.Move(itemId, to: equipment, slot: "primary");

if (await player.Inventory.CanAfford("ammo.shell", 1))
    await player.Inventory.Consume("ammo.shell", 1);
// fn authority — a cloud function, or a dedicated server holding a host key
const bag = await player.inventory.container('bag');
const equipment = await player.inventory.container('equipment');

await player.inventory.grant('ammo.shell', { count: 20 });
await bag.move(itemId, { to: equipment, slot: 'primary' });

if (await player.inventory.canAfford('ammo.shell', 1))
  await player.inventory.consume('ammo.shell', 1);
# fn authority — a cloud function, or a dedicated server holding a host key
bag = await player.inventory.container("bag")
equipment = await player.inventory.container("equipment")

await player.inventory.grant("ammo.shell", count=20)
await bag.move(item_id, to=equipment, slot="primary")

if await player.inventory.can_afford("ammo.shell", 1):
    await player.inventory.consume("ammo.shell", 1)
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

// fn authority — a cloud function, or a dedicated server holding a host key
Client->Commerce->Entitlements->Grant(FPSIdempotencyKey(GrantId), PlayerId, PSKeys::Item::AmmoShell);

// spending is an instance act on the entitlement you hold
Entitlement->Spend(FPSIdempotencyKey(SpendId), /*Amount*/ 1,
    TPSOnResult<void>::CreateLambda([](const TPSResult<void>& Result)
    {
        // short on the item is a declared refusal, not a silent no-op
        if (Result.IsRefused()) { DeclineReload(Result.Refusal()); }
    }));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
// fn authority — a cloud function, or a dedicated server holding a host key
var bag = await player.Inventory.Container("bag");
var equipment = await player.Inventory.Container("equipment");

await player.Inventory.Grant("ammo.shell", count: 20);
await bag.Move(itemId, to: equipment, slot: "primary");

if (await player.Inventory.CanAfford("ammo.shell", 1))
    await player.Inventory.Consume("ammo.shell", 1);

A sessão de um jogador faz as leituras, as movimentações e a verificação de saldo com as mesmas chamadas. Conceder, consumir e destruir não cabem a ela: a plataforma recusa essas operações como proibidas e informa qual direito falta a quem chamou, seja qual for o binding que fez a chamada.

O modelo

Um inventário não traz nenhuma noção própria. É um preset: uma forma montada com o que a entidade já oferece, então tudo abaixo é declaração de entidade, não um mecanismo desta página.

DeclaraO que é
um tipo com donoa posse pertence a um dono, e a seleção por dono é uma operação da própria entidade
uma ref para um item do catálogoa referência guarda o id do item, nunca a key, então renomear uma chave é seguro. A definição do item fica no comércio
um aspecto de pilha com incrementoa pilha muda por um delta, não sobrescrevendo o que foi lido (veja abaixo)
um teto por dono com o comportamento no limiteuma de três opções, e não existe padrão: refuse · redirect para um compartimento de dono declarado · discard with event

Passe uma chave de idempotência em todo incremento

Um incremento não é idempotente: dois incrementos são dois incrementos. Ele precisa levar uma chave de idempotência, senão um débito repetido debita duas vezes; para saber o resultado, faça uma leitura endereçada.

a grant arrives; the owner is at the capthe cap's declared behaviour (no default)the loot stays where it wasinto a declared owner bucketgone, with an event refuseredirectdiscard with event
Uma concessão numa mochila cheia: quem decide é o comportamento declarado do teto, e não existe padrão

O que vale para toda posse.

SempreO que é
o teto não tem padrãoas três respostas para uma mochila cheia são três jogos diferentes: a recusa faz o jogador perder o loot bem na frente dele, o redirecionamento é um correio ou um depósito que transborda, e o descarte é uma perda silenciosa, legítima só porque foi declarada e pode ser observada. Nenhum padrão serve para os três, então quem escolhe é a declaração
o dono é imutávelnada muda de mãos editando um campo: uma posse se move como uma revogação mais uma nova concessão com origem declarada, e os dois fatos ficam registrados
uma transferência entre dois jogadoresé outra promessa: exige custódia (escrow) e antifraude, e fica fora desta versão
uma linhaexibe um direito de uso, em vez de ser uma segunda fonte dele: o que foi comprado fica no comércio, e a linha aqui o representa

Erros

  • A instância não existe, ou um predicado a esconde: a resposta é not found nos dois casos, então uma recusa nunca revela que algo existe mas não é seu.
  • Um campo não declarado no aspecto (incluindo os aninhados) e um campo obrigatório sem valor são recusas de validação que indicam o campo.
  • A versão não bateu é uma falha de pré-condição, e vale repetir depois de ler de novo.
  • Uma escrita em nome de um jogador que não informa o jogador é uma recusa de validação, não uma escrita silenciosa como se fosse outra pessoa.
  • Falta de permissão para ler ou escrever responde forbidden, com leitura e escrita diferenciadas.

Limites

Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.

  • Instâncias por dono: pela regra declarada acima, e não existe padrão.
  • Tamanho da instância armazenada: a escrita é recusada como conflito, e a recusa indica o campo responsável e o tamanho medido. O teto é atingido por acúmulo, então dá para observar a aproximação antes da escrita que falha.
  • Frequência de mudanças numa instância: recusa por limite de frequência, com prazo para tentar de novo.
  • Tamanho da página de seleção: a página é cortada no teto e a flag “há mais” continua verdadeira; devolver menos sem a flag é proibido.