.::: :::.
:-==++++: :++++==-:
=+======+: :+======+=
==========================
==========================
:-: :-============================-: :-:
.=+++=-::=++============================++=::-=+++=.
-+=====+++==================================+++=====+-
-+====================================================+-
-====================================================-
.==================================================.
-+======+#%@%%#*==================*#%%@%#+======+-
-+=====#@@#+=+*@%+=====++++=====+%@*+=+#@@#=====+-
-+====*@@=:::::-@#=====%@@%=====#@-:::::=@@*====+-
-+====*@@-::::::@#=====%@@%=====#@::::::-@@*====+-
-+=====*@%+=-=+%#======%@@%======#%+=-=+%@*=====+-
-+======+*#%##*+=======#@@#=======+*##%#*+======+-
:=======================++=======================:
+%####***++============================++***####%+
=####%%%@@#========#%%%%%%%%%%#========#@@%%%####=
:=======#@%=======+@@********@@+=======%@#=======:
.+======*@@%%%%###%@%========%@%###%%%%@@*======+.
:++=====+****######+========+######****+=====++:
.-=++====================================++=-.
.:-=++++==========================++++=-:.
.:-===++++++++++++++++++++++===-:.
..:::------------:::..
___ _ _ _ _ ___ __ __ _ ____ _ ___ / _ \| | | | | | | |_ _| | \/ | / \ | _ \| |/ / | | | | | |_| | | |_| || | | |\/| | / _ \ | |_) | ' /| | | |_| | _ | | _ || | | | | |/ ___ \| _ <| . \|_| \___/|_| |_| |_| |_|___| |_| |_/_/ \_\_| \_\_|\_(_)
▸ JustCovino on Twitch implemented Cooties' netcode for his game Astro Yoinkers :: wishlist it on Steam!
players : the Dictionary of everyone connectedchange_level(), players_synced signalsteamInitEx(480)Steam.run_callbacks() every frameSteamMultiplayerPeerGlobal.change_level() emits a signal; Main swaps what lives under $Level.Main never dies: /root/Main/Level exists on every peer, every frame. Swaps arrive as call_local RPCs.| ENet | Steam | |
|---|---|---|
| NAT traversal | Requires port forwarding | Automatic |
| Matchmaking | Send your IP to a friend | Lobbies built in |
| Relay servers | None | Free, "Valve pays" |
| Platforms | Everywhere Godot runs | Steam only |
| Best for | Prototypes, LAN, local testing | Released games & Spacewar (480) |
In Cooties the backend is just an OptionButton. One enum, one dropdown, and the host button matches on it.
create_server(7777, 4) : port 7777, four players.
lobby_created signal to fire.
create_client(ip, 7777) : the mirror image of create_server.
host_with_lobby() then assign the same multiplayer_peer property. The game never knows the difference.
requestLobbyList() filters.
connect_to_lobby(), assign the peer, wait for the sync. ENet and Steam joins are twins.
getLobbyOwner() equals getSteamID()? That's a Steam ID, not a peer id. There are TWO id systems here...
get_unique_id() : your own peer id for this session. Every peer has a different answer.
get_remote_sender_id() : the peer id of whoever invoked this RPC. Never trust arguments for identity, ask the transport.
rpc_id(sender_id, ...) answers exactly one peer. Peer ids are how RPCs are addressed.
players Dictionary is keyed by peer id. Gameplay speaks peer id.
.rpc() with no id: tell everyone. The label follows the player to every screen.
get_steam_id_for_peer_id() →get_peer_id_for_steam_id()"authority" : the peer that owns the node"any_peer" : anyone. Validate everything."call_local" : on you AND everyone else"call_remote" : only on the other peers"reliable" : guaranteed, ordered, slower"unreliable" : fast. Might not arrive. YOLO."unreliable_ordered" : drops happen, never out of order_set_ready is "any_peer": every player toggles their own state, "call_local" updates the sender's copy too._start_game is "authority": anyone can say ready, only peer 1 can say go.multiplayer.peer_connectedsignal_set_ready.rpc(true)rpcis_ready = player_readylocal_start_game.rpc()rpcspawn_function, then only the server calls spawn(). Defer it so the scene finishes _ready first.set_multiplayer_authority(peer_id) and name = str(peer_id): node paths match on every client.SteamInit.peer.connect_to_lobby(lobby_id)localmultiplayer.peer_connected(peer_id)signalsend_player_to_server.rpc_id(1, players[me])rpc_sync_players_to_peer.rpc_id(sender_id, players)rpcplayers_synced.emit()signalThe network ticks at 30 Hz. Your monitor draws at 60+. Without help, remote players teleport 30 times a second.
"Players stomping each other into the ground. Velocity multiplying until someone gets launched across the map."- me, debugging Cooties at 2am
* Godot's built-in 2D physics = occasional cryptid behavior
* Rapier Physics: drop-in replacement, deterministic, actively maintained
* The whole migration is one project setting:
is_multiplayer_authority() == truelocal.:position :: Alwayssync_on_infection_area_body_entered(body)signal_set_player_infected.rpc(peer_id, true)rpc