Esta página ainda não foi traduzida: mostrando a versão em inglês.
Bots
Um bot entra como um jogador comum. Só o cérebro fica em outro lugar. Mesma sessão, mesma validação de entrada, mesmas regras, mesma ACL. A sala não percebe a diferença, e isso é proposital: os bots exercitam as regras reais do seu jogo, e o anticheat nunca precisa de uma exceção para bots.
Quando usar
- Os seus lobbies precisam ser preenchidos fora do horário de pico:
FillRoomcompleta as partidas até uma cota, e os bots cedem as vagas (seat) conforme chegam humanos. - Os bots precisam jogar pelas regras reais (validação de entrada, ACL, visibilidade), para que o anticheat nunca precise de uma exceção para bots.
- Você traz um cérebro (brain) externo, como uma política treinada ou um serviço, que entra por
ConnectAsBotcomo qualquer jogador. - A entidade de um jogador desconectado precisa passar para um bot e voltar quando ele reconecta, sem que a vaga ou a predição percebam.
- Dispensável quando o personagem nunca decide nada. Um NPC de diálogo sem cérebro pertence aos objetos do mundo.
Esta página trata de conectar um bot: colocá-lo em uma sala, preencher um lobby até a cota, passar uma vaga entre um bot e um humano. Escrever o cérebro que decide está em Como escrever o cérebro de um bot.
Quem faz o quê
| Ator | O que faz nesta página |
|---|---|
bot-brain | conecta como jogador; recebe a percepção; envia comandos |
room-owner | declara perfis, preenche salas (room) até a cota, faz a troca entre bot e humano |
Resumo
filler: números de dificuldade, um cérebro baseado em utilidade e FillRoom até uma cota[BotProfile("filler")]
[Brain(Kind.Utility)]
public static class Filler
{
public static Difficulty Difficulty = Difficulty.Of(reactionMs: 250, aimJitter: 0.08f);
[Consider(Targeting.NearestEnemy)] public static Behaviour Target;
[Steer(Steering.SeekAndStrafe)] public static Behaviour Move;
[UseAbilities(When.Ready)] public static Behaviour Fire;
}
PlayServ.Bots.FillRoom("battle", toQuota: 8, profile: "filler", minHumans: 1);@BotProfile('filler')
@Brain({ kind: 'utility' })
export class Filler {
static difficulty = Difficulty.of({ reactionMs: 250, aimJitter: 0.08 });
@Consider(Targeting.nearestEnemy) target: Behaviour;
@Steer(Steering.seekAndStrafe) move: Behaviour;
@UseAbilities(When.ready) fire: Behaviour;
}
PlayServ.bots.fillRoom('battle', { toQuota: 8, profile: 'filler', minHumans: 1 });@bot_profile("filler")
@brain(kind="utility")
class Filler:
difficulty = Difficulty.of(reaction_ms=250, aim_jitter=0.08)
target = consider(Targeting.NEAREST_ENEMY)
move = steer(Steering.SEEK_AND_STRAFE)
fire = use_abilities(When.READY)
playserv.bots.fill_room("battle", to_quota=8, profile="filler", min_humans=1)Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
USTRUCT(PSBotProfile = "filler", PSBrain = (Kind = "Utility"))
struct FFiller
{
GENERATED_BODY()
UPROPERTY(PSDifficulty = (ReactionMs = 250, AimJitter = "0.08"))
FPSDifficulty Difficulty;
UPROPERTY(PSConsider = (Targeting = "NearestEnemy")) FPSBehaviour Target;
UPROPERTY(PSSteer = (Steering = "SeekAndStrafe")) FPSBehaviour Move;
UPROPERTY(PSUseAbilities = (When = "Ready")) FPSBehaviour Fire;
};
// declarations compile into the same pushed model — playserv push from the UE project or CI
// a host tops up the room it serves
Client->Bots->FillRoom(PSKeys::Rooms::Battle,
FPSFillRoomParams{ .ToQuota = 8, .Profile = TEXT("filler"), .MinHumans = 1 });
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.[BotProfile("filler")]
[Brain(Kind.Utility)]
public static class Filler
{
public static Difficulty Difficulty = Difficulty.Of(reactionMs: 250, aimJitter: 0.08f);
[Consider(Targeting.NearestEnemy)] public static Behaviour Target;
[Steer(Steering.SeekAndStrafe)] public static Behaviour Move;
[UseAbilities(When.Ready)] public static Behaviour Fire;
}
PlayServ.Bots.FillRoom("battle", toQuota: 8, profile: "filler", minHumans: 1);Um cérebro externo (uma IA mais pesada, uma política treinada, um serviço) conecta como qualquer jogador:
ConnectAsBot coloca um cérebro externo na sala como jogador: os mesmos deltas entram, os mesmos inputs saemvar bot = await PlayServ.ConnectAsBot(projectKey, botId: "trainer-07");
var seat = await bot.Matchmaking.Find("battle");
var room = await bot.Rooms.Join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputsconst bot = await PlayServ.connectAsBot(projectKey, { botId: 'trainer-07' });
const seat = await bot.matchmaking.find('battle');
const room = await bot.rooms.join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputsbot = await PlayServ.connect_as_bot(project_key, bot_id="trainer-07")
seat = await bot.matchmaking.find("battle")
room = await bot.rooms.join(seat)
# perception in ← the same deltas a player receives; commands out ← the same inputsAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
// An Unreal-based trainer client is a legitimate brain — it connects as a player.
FPlayServClient::ConnectAsBot(ProjectKey, TEXT("trainer-07"),
TPSOnResult<FPlayServClient*>::CreateLambda([](const TPSResult<FPlayServClient*>& Result)
{
if (!Result.HasValue()) { return; }
FPlayServClient* Bot = Result.Value();
Bot->Matchmaking->Of<FBattleQueue>()->Tickets->Create(FPSTicketClaim{ .Mode = TEXT("battle") },
TPSOnResult<FPSTicket*>::CreateLambda([Bot](const TPSResult<FPSTicket*>& TicketResult)
{
if (!TicketResult.HasValue()) { return; }
TPSSubscription Placement = TicketResult.Value()->Subscribe(
[Bot](const FPSSeat& Seat) { Bot->Rooms->Join(Seat); });
}));
}));
// perception in ← the same deltas a player receives; commands out ← the same inputs
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.var bot = await PlayServ.ConnectAsBot(projectKey, botId: "trainer-07");
var seat = await bot.Matchmaking.Find("battle");
var room = await bot.Rooms.Join(seat);
// perception in ← the same deltas a player receives; commands out ← the same inputsO modelo
Um bot não traz nenhum conceito próprio. Ele é uma credencial de ator que usa o que salas, dados e movimentação já declaram.
O que a declaração de um bot contém.
| Declara | O que é |
|---|---|
tick de raciocínio | com que frequência os cérebros são consultados; não é o tick da simulação: os cérebros rodam fora, e uma chamada de rede a cada tick é inviável |
onde ficam os cérebros | onde eles executam — uma cloud function, o backend do estúdio, o servidor de jogo dele. Qual deles não faz parte do contrato, e trocar de um para outro não é uma mudança incompatível |
preset de ator | os direitos do bot, como um preset de ator comum |
visibilidade do marcador de bot | se os participantes ficam sabendo. O marcador sempre existe, e a plataforma sempre o observa; se os jogadores o veem é uma declaração do tipo de sala, porque em alguns mercados revelar um oponente de IA é obrigação e, em outros, uma escolha de produto |
comportamento quando os cérebros estão indisponíveis | um de três, sem padrão: do nothing · leave the room · fall back to built-in default behaviour |
preenchimento da lista de participantes | declarado pelo tipo de sala — quantos, em que condição, até que momento. O Matchmaking não sabe nada de bots: ele junta atores e não decide com quem completar a partida |
O que vale para todo bot.
| Sempre | O que é |
|---|---|
um ator, não um jogador | tem uma credencial de ator, mas não tem provedor de login, vínculos nem sessões |
posse econômica | nenhuma: sem direitos de uso, sem compras, sem entradas no ranking |
percepção | é a de um jogador: a mesma zona de visibilidade, o mesmo predicado, o mesmo limite de objetos. Um bot e um jogador na mesma posição recebem o mesmo conjunto de objetos, então um bot não consegue usar wallhack mais do que um jogador |
percepção ampliada | é um preset de ator, não uma propriedade do bot: um modo de debug ou de “treinador onisciente” é declarado como um preset com um predicado mais amplo |
entre um raciocínio e outro | vale o último comando, e o destino dele é o que o tipo de movimento já declara para input obsoleto — um bot cujo cérebro está pensando é o mesmo caso de um jogador que perdeu a conexão |
capacidade da sala | conta o bot: ele ocupa uma vaga como qualquer outro |
Erros
- Sala que não aceita bots é um conflito, e repetir não adianta.
- Limite de bots esgotado é um conflito, não forbidden — a permissão para colocar um bot existe; a sala é que está cheia. Vale repetir quando a sala liberar espaço.
- Comando para um bot vindo de um ator sem a permissão responde forbidden, e não adianta repetir.
- Cérebros indisponíveis não são um erro — é um dos três comportamentos declarados acima.
- Declarado no deploy, recusado no deploy: um bot indicado como dono de uma entrada no ranking e a falta de um tick de raciocínio são falhas de validação no deploy, e não surpresas numa sala ao vivo.
Limites
Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.
- Bots em uma sala: colocar mais um é recusado como conflito; bots existentes nunca são removidos para abrir espaço.
- Bots por projeto: o mesmo conflito.
- Piso do tick de raciocínio: uma declaração mais rápida que o piso é recusada no deploy, porque uma chamada de rede por tick é inviável.
- Taxa de comandos para um bot: rate limit com prazo.
- Prazo de resposta dos cérebros: quando ele expira, vale o comportamento declarado para indisponibilidade.