-
Notifications
You must be signed in to change notification settings - Fork 5
Events
znet reports everything through one callback per object. You set it once, and dispatch inside it by type.
void OnEvent(Event& event) {
EventDispatcher dispatcher{event};
dispatcher.Dispatch<IncomingClientConnectedEvent>(
ZNET_BIND_GLOBAL_FN(OnClientConnected));
dispatcher.Dispatch<IncomingClientDisconnectedEvent>(
ZNET_BIND_GLOBAL_FN(OnClientDisconnected));
}Each Dispatch<T> runs its function only if the event is a T. Handlers return
bool, which is ORed into event.handled(). Dispatch is by type alone, so
returning true does not stop the Dispatch calls after it, and nothing inside
znet reads the flag. false is the usual answer; true is only worth
returning if your own code reads handled() afterwards.
ZNET_BIND_GLOBAL_FN wraps a free function; ZNET_BIND_FN wraps a member
function, capturing this.
| Event | When | Accessors |
|---|---|---|
ServerStartupEvent |
The listener is up, before any connection | server() |
IncomingClientConnectedEvent |
A session finished its handshake and is ready, just before a worker takes it over | session() |
IncomingClientDisconnectedEvent |
A session that had become ready ended | session() |
ServerShutdownEvent |
The listener is closing, before sessions are torn down | server() |
IncomingClientConnectedEvent is where a session gets its codec and handler.
It fires once the handshake is done, so encryption is already negotiated and the
session can send immediately. The acceptor fires it before handing the session
to a worker, so nothing is dispatching on the session while you install them. It
is also where per-connection state of your own is attached; see
Session State.
A session that dies before becoming ready produces no
IncomingClientDisconnectedEvent, because it never produced a connected event
either. Events come in pairs, so you do not have to track half-open connections
to keep your own bookkeeping balanced.
Shutdown is the one exception: sessions still open when the server stops are
closed without a disconnect event, so ServerShutdownEvent is the last thing
you hear. Do that bookkeeping there rather than expecting a final round of
disconnects.
| Event | When | Accessors |
|---|---|---|
ClientConnectedToServerEvent |
The handshake completed | session() |
ClientDisconnectedFromServerEvent |
The session ended, after having connected | session() |
ClientConnectionFailedEvent |
The session existed but died before it was ready | session() |
The pairing holds on the client too: a disconnect event only ever follows a
connect event, and a deliberate Disconnect() on a ready session still produces
one. A session that dies or runs past connection_timeout while still
handshaking fires ClientConnectionFailedEvent instead.
Everything that fails before a session exists is Connect()'s return value
rather than an event: TCP connects synchronously, so a refused port or an
unreachable host comes straight back, and ZDT runs its cookie exchange and MTU
probe inside Connect() too, so a server that never answers, is full, or speaks
another protocol version fails there. Calling Disconnect() yourself before the
session is ready counts as a failure and fires the event too. Destroying the
client instead races its stop request against the loop, so the event may or may
not arrive; do not depend on it during teardown.
Covered in Peer-to-Peer: PeerLocatorReadyEvent,
PeerConnectedEvent, PeerLocatorFailedEvent, PeerLocatorCloseEvent.
p2p::Host, the shared socket everything punches from, is the exception: it
reports each gather through the GatherCallback passed to Gather and each
punch through the PunchCallback passed to Punch, rather than through
events. Both run on the host's own thread, the punch one only once the
punched session has finished its handshake, so setting a codec and handler
inside it is the same pattern as a connected event. p2p::PeerLocator is
what turns those callbacks into the events above.
The callback does not run on the thread that created the server or client.
- Server: the acceptor thread for
ServerStartupEvent,ServerShutdownEventandIncomingClientConnectedEvent, since a session is still the acceptor's when it becomes ready; the worker that owns the session forIncomingClientDisconnectedEvent - Client: the client's loop thread
So anything your handler touches is shared state, and needs its own synchronization. Full rules in Threading Model.
Any type deriving from Event and carrying the two class macros can go through
the same dispatcher:
class MatchStartedEvent : public Event {
public:
ZNET_EVENT_CLASS_TYPE(MatchStartedEvent)
// categories are bit flags and znet defines three, EventCategoryServer,
// EventCategoryClient and EventCategoryP2P, so start your own above them
ZNET_EVENT_CLASS_CATEGORY(1 << 3)
};Useful for routing your own state changes through the same callback rather than maintaining a second path beside it.