Networking
One player’s game is the server, the authority on what happens; the others are clients, connected to it. The server sends each client the state of the entities it should see.
Hosting and joining
Section titled “Hosting and joining”function Lobby:host() Net.startServer(7777)end
function Lobby:join(address) Net.connect(address, 7777)endvoid host(){ Net::startServer(7777);}
void join(const string &in address){ Net::connect(address, 7777);}startServer(port)hosts a game that this player also plays in;startDedicatedServer(port)hosts one with no player.connect(host, port)joins one;disconnect()leaves.isServer()andisClient()say which role this game has, andpeerCount()how many connections it has.canHost()says whether this build can host at all. A game in a web browser cannot, because a browser cannot listen for players, so offer Host only when it is true. Joining works in a browser.
startServer, startDedicatedServer and connect return false when they fail, for example when
the port is already in use.
Joining a host on another map
Section titled “Joining a host on another map”A player who joins from a title screen does not have the host’s map loaded yet, and the state the
host sends needs the map’s entities to land on. The host calls waitForReady(true) when it starts. A player who joins then receives messages
(for example an RPC that names the host’s map) but no state until it calls ready(), once that
map has loaded. The state then arrives as one full snapshot. The Masquerade sample joins this way:
it connects from its title screen, the host’s welcome message names the map, and the joining
player loads it and reports that it is ready.
waitForReady(wait), on the host, sets whether players who join wait for theirready(). It is off by default, so state is sent as soon as a player joins.ready(), on a client, tells the host that the host’s scene is loaded.
Desktop games talk over UDP. A host also listens on port + 1 (7778 above) for players in a web
browser, who have no UDP. The web build makes the same connect call with the game port and
reaches the host on port + 1 by itself.
Replicated entities
Section titled “Replicated entities”An entity that every player should see the same gets two components:
- Network Identity gives it an identity that every player shares. Replication assigns the identity; you do not set it yourself.
- Networked Transform sends its position, rotation and scale from the server to the clients, which smooth the movement between updates.
Move the entity on the server, and the clients see it move.
A script reads an entity’s identity with NetworkComponent.of(entity) (NetworkComponent::of
in AngelScript). Its authority says whether the server or a client owns it, owner is the
owning player’s id, and id is its network id. All three are read-only.
The server can also add a networked entity while the game runs. Net.spawn(prefab, x, y, z)
spawns the prefab as scene.spawn does, and every client spawns its own copy, including a player
who joins later. Give the prefab’s root a Networked Transform so its movement follows. On a
client, spawn creates nothing and returns an entity that is not valid.
Messages
Section titled “Messages”Net.rpc(name) sends a message, Net.rpcNumber(name, value) sends one with a number, and
Net.rpcText(name, text) sends one with text (Net::rpc and so on in AngelScript). A message
from a client goes to the server, and a message from the server goes to every client.
Net.on(name, handler) receives them. The handler gets the sender’s player id, then the number or
the text if the message carries one. On the server, the sender is the client that sent it; on a
client, the sender is always the server (0). You can register handlers at any time, including
before hosting or joining. Registering a second handler for the same name replaces the first.
Net.on("score", function(sender, value) Log.info("player " .. sender .. " scored " .. value)end)Net.on("ping", function(sender) end)void onScore(int sender, double value){ Log::info("player " + sender + " scored " + value);}
void launch(){ NetNumberHandler @score = onScore; Net::on("score", score);}In AngelScript a handler is a NetHandler (the sender only), a NetNumberHandler (the sender
and a number) or a NetTextHandler (the sender and a const string &in). The engine declares
these types, so a script does not.
Replicated properties
Section titled “Replicated properties”A behavior’s property can be kept the same on every machine by marking it replicated. On a networked entity (one with a Network Identity, spawned by the server), the server sends the value whenever it changes, and each client’s copy of the behavior receives it, including the copy of a player who joins later.
List the names on the class:
Guest.replicated = { "activity", "costume" }Add replicated after the default and description:
[0, "What the guest is doing", replicated] int activity;[0, "Which costume", replicated] int costume;Numbers, booleans, strings, colours and Float3 values can be replicated. An entity or asset
property cannot. Clients run the same behaviors,
so change a replicated property only on the server (if Net.isServer()) and let clients only
display it.
Players
Section titled “Players”Each player has an id. The server is 0, and each client gets a number from the server when it
joins. Net.localPlayer() returns your own id, or -1 while offline and until the server has
welcomed you.
Net.onJoin(handler)andNet.onLeave(handler)call the handler with a player’s id. On the server they fire when a client joins or leaves. On a client,onJoinfires once with its own id when it has joined, andonLeavefires with 0 if the server goes away.Net.rpcTo(player, name),Net.rpcNumberTo(player, name, value)andNet.rpcTextTo(player, name, text)send from the server to one client. They do nothing on a client.Net.spawnFor(player, prefab, x, y, z)spawns asNet.spawndoes, with the entity owned by that player.Net.isMine(entity)tells a player whether an entity is theirs, andNetworkComponent.of(entity).owneris its owner’s id on every machine.
Because a message’s sender is the player who sent it, the server can check that the player is
allowed to do what the message asks before acting on it. When a player leaves, your onLeave
handler decides what happens to the entities they owned.
What is missing
Section titled “What is missing”- Prediction. A client shows the server’s state as it arrives, with no prediction or lag compensation, so players feel the delay in fast action.
- Encryption. Traffic between desktop games over UDP is not encrypted, and the host’s browser
port speaks plain WebSocket. A web game served over https must join with a
wss://address, which needs a TLS proxy in front of the host. - Editor controls. Hosting and joining happen only from scripts; the editor has no networking settings. To test with two players, run the game with Play, then choose Game > Play New Instance to open a second Game tab with its own copy of the game, and host in one and join from the other.
