good_net 0.3.0
good_net: ^0.3.0 copied to clipboard
Networking for the good game engine: declared messages built on good's record layer, sessions, reliable and unreliable channels, and the transport contract a backend implements.
good_net #
Networking for the good engine: declared messages, sessions, and the
transport contract a backend implements. It sits beside the kernel, so goo2d
and goo3d games share the same session and messaging plumbing.
This is the command API, over a socket. A network message and a
GameCommand are the same thing — a typed record, declared once,
identified on the wire by its position in that declaration, handed to a
handler registered where it runs. So they are not two implementations:
NetMessage and NetSignal are spelled exactly like SinkCommand and
SignalCommand, and the record layer underneath (ParamDescriptor,
ParamPointer, ParamBatch, ParamBuffer) is good's own, reused instead of
reimplementing it here.
class MyState extends GameState2D<MyGame> with MultiplayerState<MyGame> {
late final Fire fire;
@override
void describeNetwork(NetDescriptor descriptor) {
descriptor.transport(P2PNetTransport());
fire = descriptor.has(Fire(), channel: NetChannel.unreliable);
descriptor.hasHandler(fire, _onFire);
}
void _onFire(({double angle}) params, NetPeerId from) =>
spawnBullet(from, params.angle);
}
fire((angle: 1.2));
What networking adds over commands is the two facts an isolate boundary does not have, both declared instead of passed at the send site:
NetTarget— which machine handles it.hostis a client's intent and runs locally when the host sends it, which is what makes single-player, hosting and joining one code path.clientsandeveryoneare the host's decisions.NetChannel—reliable(arrives, in order, however many retransmissions it takes) orunreliable(sent once, for state that supersedes itself, like a transform every tick).
What is here #
- Message shapes, the
describeNetworkpass, andMultiplayerState. NetworkSystem, which drains what arrived at the top of each fixed tick and flushes what was queued once per frame — so delivery is as deterministic as the rest of the simulation.NetSession, the roster, andNetPeerListener/NetSessionListener.NetTransport, the backend contract.LoopbackNetTransport, which is in-process and real, not a mock. It is what tests and split-screen run on, and it is why a multiplayer game can be developed without a second machine.package:good_net/testing.dart— a conformance suite every backend is run against, so "implementsNetTransport" means the same thing for all of them.
What is not #
- Request/reply, deliberately. A
GameCommand<P, R>can await a result because the other isolate answers on a known schedule; a remote peer may never answer at all, and an API that looks awaitable but can hang forever is worse than one that does not offer it. - The ECS replication layer — a
Replicatedmixin, delta compression, prediction and reconciliation. That is its own topic built on these interfaces; the channel split is exactly the primitive it needs.
Backends #
good_net_p2p is the one that reaches another machine, with
no server to host. The Steam backend was dropped instead of built; the
contract is open, so one remains possible as a separate package and nothing
here assumes it.
See the networking guide for the full reference.