Phase 3 of RFC-PLUGIN-EVENTS. Tracker: flow-codegen#11.
Scope
The flow-codegen-side of the new OnEvent form, plus the new Emit node, plus the legacy converter.
flow_io.zig
Event.OnEvent gains name: ?[]const u8 = null. module/callback/params become optional. buildEvent accepts either form; both-set or neither-set is error.MalformedFlow.
- Add
NodeKind.Emit = struct { event: []const u8 }. buildNodeKind parses it; validate accepts it (codegen rejects unknown event names with a sourced diagnostic).
- Round-trip the new fields through
renderFlowJsonc / parseFlow.
codegen.zig
- New-form
OnEvent (renderEventEntry): resolve the dotted name through the assembler's comptime resolver (phase 1 surface). Reflect the payload's fields via @typeInfo(VariantType).@"struct".fields. Emit a FlowEventHandler struct with game_ptr: *anyopaque = undefined and a method body that is the existing flow lowering. Lift anyNodeNeedsEntity and Subflow rejections for new-form flows (game is in scope via downcast of game_ptr).
- Legacy-form
OnEvent: keep the current setup()/flowEvent/raw-slot path verbatim. Removed in phase 6.
Emit node: lower to game.emit(.{ .<qualified_tag> = .{ .field = pin, ... } });. Inputs are reflected payload fields; unwired field = error.DanglingPin.
Converter
Ship a legacy_onevent_to_name converter that rewrites legacy OnEvent blocks to the name form once module+callback resolve through the merged-union variant map. Parallel to the .flow.zon→.flow.jsonc converter shipped by RFC-FLOWS-JSONC.
Success criteria
- New-form
OnEvent codegens a FlowEventHandler struct that is assignable into the GameHooks receiver tuple (phase 4 picks them up).
Emit lowers to a single game.emit statement; payload field types round-trip.
- Legacy form codegens unchanged.
- Unknown event name → sourced
error.UnknownEvent (file + node id in the diagnostic).
- Tests for new-form, legacy form, Emit node, unknown event, and the converter.
Blocked on
References
- RFC: §7 (flow-codegen resolution), §8 (Emit node), §5 (
game access via game_ptr).
- Existing
renderEventEntry: flow-codegen/src/codegen.zig:517-575.
- v1
OnEvent: flow-codegen/src/flow_io.zig:60-66.
Phase 3 of RFC-PLUGIN-EVENTS. Tracker: flow-codegen#11.
Scope
The flow-codegen-side of the new
OnEventform, plus the newEmitnode, plus the legacy converter.flow_io.zigEvent.OnEventgainsname: ?[]const u8 = null.module/callback/paramsbecome optional.buildEventaccepts either form; both-set or neither-set iserror.MalformedFlow.NodeKind.Emit = struct { event: []const u8 }.buildNodeKindparses it;validateaccepts it (codegen rejects unknown event names with a sourced diagnostic).renderFlowJsonc/parseFlow.codegen.zigOnEvent(renderEventEntry): resolve the dotted name through the assembler's comptime resolver (phase 1 surface). Reflect the payload's fields via@typeInfo(VariantType).@"struct".fields. Emit aFlowEventHandlerstruct withgame_ptr: *anyopaque = undefinedand a method body that is the existing flow lowering. LiftanyNodeNeedsEntityandSubflowrejections for new-form flows (gameis in scope via downcast ofgame_ptr).OnEvent: keep the currentsetup()/flowEvent/raw-slot path verbatim. Removed in phase 6.Emitnode: lower togame.emit(.{ .<qualified_tag> = .{ .field = pin, ... } });. Inputs are reflected payload fields; unwired field =error.DanglingPin.Converter
Ship a
legacy_onevent_to_nameconverter that rewrites legacyOnEventblocks to thenameform oncemodule+callbackresolve through the merged-union variant map. Parallel to the.flow.zon→.flow.jsoncconverter shipped by RFC-FLOWS-JSONC.Success criteria
OnEventcodegens aFlowEventHandlerstruct that is assignable into theGameHooksreceiver tuple (phase 4 picks them up).Emitlowers to a singlegame.emitstatement; payload field types round-trip.error.UnknownEvent(file + node id in the diagnostic).Blocked on
References
gameaccess viagame_ptr).renderEventEntry:flow-codegen/src/codegen.zig:517-575.OnEvent:flow-codegen/src/flow_io.zig:60-66.