good_net 0.1.0
good_net: ^0.1.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 family: declared messages, sessions, and the
transport contract a backend implements. Dimension-agnostic — not under
goo2d, because a future goo3d game needs the same session and messaging
plumbing.
This is the command API, over a socket. A network message and a
GameCommand are the same idea twice — 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 rather
than reimplemented.
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 rather than 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 rather than 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.