The gamemode decides when and how a connected player spawns
Status: Accepted (2026-06-30)
Context
The handshake and the spawn are separate steps (ADR 0022): after ServerHello the player is connected but not in the world (PreSpawn), and its client repeats RequestSpawn until a SpawnPlayer grant arrives. Something has to decide when that grant is given and with which point and appearance.
Gamemodes do very different things before a spawn: a login screen, a skin or team choice, a tutorial, nothing at all. A built-in class-selection screen would hard-code one of these flows into the platform. What the platform must provide is a primitive that spawns a player and an event that says a player is waiting.
Decision
- No automatic spawn. A connected player stays in PreSpawn until the gamemode spawns it.
player_connect(player)starts the pre-spawn flow. The Lua event is raised once for each new player, when it joins as a guest or when the auth resource accepts it (ADR 0052). A repeatedClientHellodoes not raise it again.player:spawn{skin, variant, x, y, z, heading, interior}spawns (facadespawn_player(id, point, skin, variant)). The gamemode chooses the point and the appearance; without a position it is the spawn policy point (ADR 0012),variantdefaults to 1. The skin must be in the model catalog, the variant non-zero, the point valid. The call is accepted only in PreSpawn (PlayerAlreadySpawnedotherwise).- The call arms, the client pulls.
spawnsends nothing: it stores the spawn info on the player. The nextRequestSpawnis answered withSpawnPlayercarrying exactly that info; until the call it is answered with nothing. A repeated request before the player's first living frame gets the same grant again, which covers a lostSpawnPlayer. After that, and during death and respawn, the request is ignored: it is not a second spawn path. - The repeating request is the PreSpawn keepalive. Its session token is validated, which refreshes the player's liveness, so a player who spends minutes on a login or skin screen is not timed out. The other direction is ServerKeepalive.
- The grant fixes the appearance and the scope. The player takes the skin and variant from the call, and its interior scope from the point.
player_spawn(player)is raised on the player's first living frame after the grant, the same event as after a respawn.
Consequences
- No wire change and no new packet:
RequestSpawnandSpawnPlayerstay as they are; only the authority over the grant moved to the gamemode. - A gamemode that never calls
spawnleaves players in PreSpawn forever. Every gamemode must spawn its players. - A skin picker, a team choice or a login is gamemode content built on
player_connectandspawn, not a platform feature.
Alternatives considered
- Class selection in the core (a list of classes and a picker on the client). Rejected: it hard-codes one narrow flow into the platform and leaves gamemodes with other flows to work around it.
- Push the grant from the server when
spawnis called. Rejected: an unreliable push may be lost, and the reliable lane resets its receive state on reconnect; it would need a new retry channel. The client's repeating request already is one, and it doubles as the keepalive. - A spawn picker built into the client. Rejected: a client feature that only some gamemodes want, decided by the client instead of the server.
