Skip to content

version 0.40.0 - connection-safe service handles, stale clients fail loudly

Choose a tag to compare

@dmitry-lebedev dmitry-lebedev released this 17 Sep 15:38
· 19 commits to main since this release

Added

  • MetaRef<T> — a handle to a service on an entity, safe to hold for the process lifetime. Stores the lookup key, not the client, and re-resolves per access. Current / TryGet are the allocation-free path; GetAsync() completes synchronously once subscribed.
  • MetaClient.PlayerRef<T>() (follows PlayerId across a relogin) and MetaClient.Ref<T>(entityId).
  • MetaRefFactory<T> for services whose entity id is only known at runtime.
  • Generated per-assembly MetaRefs container — one property per service: MetaRef<T> for UserOwned, MetaRefFactory<T> for the rest.

Changed

  • Inbound broadcasts route through a flat array indexed by MethodId instead of a dictionary.

Fixed

  • ValueTask service methods were mishandled by the client generator. Only Task counted as awaitable, so the awaitable was taken for the result (Task<ValueTask>) and every replay path invoked the method without awaiting it, with no EnsureSyncCompletion on the broadcast path. Task and ValueTask are now treated alike, matching the server API, contract API and desync formatting. BroadcastValidator.EnsureSyncCompletion gained a ValueTask overload.
  • DispatcherNetworkAdapter never implemented IDisposable while the teardown gates on Network is IDisposable, so its cleanup had never run — every disconnected entity leaked a dispatcher broadcast subscription and an OnDisconnected handler onto the long-lived connection.
  • A failed fire-and-forget send from Optimistic / CrossOptimistic is now logged with its service and method. The continuation only handled the success case, so any failed optimistic send was dropped and its exception surfaced later, if ever, as an unattributed UnobservedTaskException
  • The full list of changes is in Сhanges.md.