Saltar al contenido principal
Servicios alrededor del juego

Esta página aún no está traducida: se muestra la versión en inglés.

Inventory

Un solo conjunto de filas con dueño que usan los disparos, el botín, las habilidades y el desplazamiento. Las pilas cambian por incremento, y el tope por dueño se comporta en el límite como tú elijas.

Cuándo usarlo

  • Los jugadores tienen cosas, y cada posesión es una fila con dueño: se lee por dueño, tiene un tope por dueño y lo que pasa al desbordarse se declara, no viene por defecto.
  • Una cantidad se acumula: una pila (stack) cambia mediante un incremento con clave de idempotencia, así que un descuento que se reintenta no descuenta dos veces.
  • Los demás módulos gastan de un mismo conjunto: los disparos descuentan munición, el botín otorga objetos y las compras aparecen como filas vinculadas a su derecho de uso (entitlement).
  • No lo necesitas si nadie puede poseer ese número. Los HP, la XP y los tiempos de recarga (cooldown) van en las estadísticas.

Quién hace qué

ActorQué hace en esta página
playerlee lo que posee y gasta de ahí
backend-serviceotorga, incrementa y revoca en nombre de un jugador, e indica en nombre de quién actúa

En resumen

Con autoridad fn: otorga munición, mueve un artículo al espacio de equipo primary y comprueba que alcance 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)
Próximamente — Go

Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.

// 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);

Una sesión de jugador hace las lecturas, los movimientos y la comprobación de saldo con las mismas llamadas. Otorgar, consumir y destruir no le corresponde: la plataforma lo rechaza como forbidden e indica qué permiso le falta a quien llama, sin importar desde qué binding se hizo la llamada.

El modelo

Un inventario no introduce conceptos propios. Es un preset: una forma armada con lo que la entidad ya ofrece, así que todo lo que sigue es una declaración de entidad y no un mecanismo propio de esta página.

DeclaraQué es
un tipo con dueñola posesión pertenece a un dueño, y la selección por dueño es una operación propia de la entidad
un ref a un artículo del catálogola referencia guarda el id del artículo y nunca su key, así que renombrar una clave es seguro. La definición en sí vive en Catalog & Commerce
un aspecto de pila con incrementouna pila cambia por una delta, no sobrescribiendo lo que se leyó (ver abajo)
un tope por dueño con su comportamiento en el límiteuno de tres, y no hay valor por defecto: refuse · redirect hacia un depósito de dueño declarado · discard with event

Pasa una clave de idempotencia con cada incremento

Un incremento no es idempotente: dos incrementos son dos incrementos. Debe llevar una clave de idempotencia; si no, un descuento reintentado descuenta dos veces. Para conocer el resultado, haz una lectura dirigida.

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
Otorgar a una bolsa llena: decide el comportamiento declarado del tope, y no hay valor por defecto

Qué se cumple para toda posesión.

SiempreQué es
el tope no tiene valor por defectolas tres respuestas ante una bolsa llena son tres juegos distintos: un rechazo hace que el jugador pierda el botín frente a sus ojos, una redirección es un correo o un almacén que se desborda, y un descarte es una pérdida silenciosa, legítima solo porque se declaró y se puede observar. Ningún valor por defecto sirve para los tres, así que decide la declaración
el dueño es inmutablenada cambia de manos editando un campo: una posesión se mueve como una revocación más un nuevo otorgamiento con origen declarado, y ambos hechos quedan registrados
una transferencia entre dos jugadoreses otra promesa: necesita custodia (escrow) y antifraude, y queda fuera de esta versión
una filamuestra un derecho de uso, no es una segunda fuente de él: lo comprado vive en Catalog & Commerce, y la fila de aquí lo representa

Errores

  • La instancia no existe o un predicado la oculta: en ambos casos la respuesta es not found, así que un rechazo nunca revela que algo existe pero no es tuyo.
  • Un campo que no está declarado en el aspecto (incluidos los anidados) y un campo obligatorio sin valor son rechazos de validación que nombran el campo.
  • La versión no coincidió: es una falla de precondición, y vale la pena repetir después de volver a leer.
  • Una escritura en nombre de un jugador que no lo nombra es un rechazo de validación, no una escritura silenciosa como si fuera otra persona.
  • Sin permiso para leer o escribir, la respuesta es forbidden, y distingue la lectura de la escritura.

Límites

Las cifras llegarán con el capítulo sobre los límites de la plataforma; lo que pasa al alcanzar cada tope ya está definido.

  • Instancias por dueño: según la regla declarada arriba, y no hay valor por defecto.
  • Tamaño de la instancia guardada: la escritura se rechaza como conflicto, y el rechazo nombra el campo culpable y el tamaño medido. Al tope se llega por acumulación, así que puedes ver que se acerca antes de la escritura que falla.
  • Frecuencia de cambios en una instancia: un rechazo por límite de frecuencia que indica cuándo volver a intentar.
  • Tamaño de página de una selección: la página se recorta al tope y la marca de “hay más” sigue en verdadero; devolver menos filas sin esa marca está prohibido.