From a wire packet to a plugin event

From a wire packet to a plugin event A sequence diagram generated by Archify. OnCallStub postfix one HarmonyX hook; header read once, unsubscribed method ids rejected route SyncNearEntities TryReadAppearAndDisappear appears + disappears ICombatEventSink calls e.g. ReplaceEntityBuffs → EnqueueEvent onto a ConcurrentQueue Game.Update postfix Drain() CombatEventOccurred e.g. CombatEvent.EntityBuffsSeeded, SpecChanged BuffsFor / TryGetTalentSpec read-only snapshot Network receive thread Main thread Game client · ZRpc + Update · Sequence participant Game client ZRpc + Update Stub dispatcher · WorldNtfStubDispatcher · Sequence participant Stub dispatcher WorldNtfStubDispatcher Combat probe · PandaCombatStubProbe · Sequence participant Combat probe PandaCombatStubProbe Entity reader · SyncNearEntitiesReader · Sequence participant Entity reader SyncNearEntitiesReader CombatService · Application · Sequence participant CombatService Application Service tick · Host · Sequence participant Service tick Host Your plugin · IPluginServices · Sequence participant Your plugin IPluginServices Legend request return async trace default message

Network thread

  • • Parse and queue only — never call plugins
  • • Never throw across the IL2CPP boundary
  • • Readers are pure functions over bytes, so they unit-test offline

Main thread

  • • One Drain per Game.Update fans events out
  • • Plugins may touch Unity safely here
  • • Snapshots handed to plugins are read-only

Adding a wire field

  • • Decode it in the Protobuf reader
  • • Carry it through ICombatEventSink
  • • Expose it as a CombatEvent or service member in Abstractions