Skip to main content

proof_engine/editor/
event_editor.rs

1
2//! Event system editor — visual event graph, publisher/subscriber wiring,
3//! event timeline, dispatch tracing, priority queues, and event replay.
4
5use glam::{Vec2, Vec4};
6use std::collections::HashMap;
7
8// ---------------------------------------------------------------------------
9// Event value types
10// ---------------------------------------------------------------------------
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum EventParamType {
14    Void,
15    Bool,
16    Int,
17    UInt,
18    Float,
19    Double,
20    String,
21    Vec2,
22    Vec3,
23    Vec4,
24    Quaternion,
25    EntityId,
26    AssetRef,
27    Color,
28    Bytes,
29    Json,
30    Custom,
31}
32
33impl EventParamType {
34    pub fn label(&self) -> &'static str {
35        match self {
36            Self::Void => "void",
37            Self::Bool => "bool",
38            Self::Int => "int",
39            Self::UInt => "uint",
40            Self::Float => "float",
41            Self::Double => "double",
42            Self::String => "string",
43            Self::Vec2 => "vec2",
44            Self::Vec3 => "vec3",
45            Self::Vec4 => "vec4",
46            Self::Quaternion => "quat",
47            Self::EntityId => "EntityId",
48            Self::AssetRef => "AssetRef",
49            Self::Color => "Color",
50            Self::Bytes => "bytes",
51            Self::Json => "JSON",
52            Self::Custom => "Custom",
53        }
54    }
55
56    pub fn size_bytes(&self) -> usize {
57        match self {
58            Self::Void => 0,
59            Self::Bool => 1,
60            Self::Int | Self::UInt | Self::Float => 4,
61            Self::Double | Self::EntityId => 8,
62            Self::Vec2 => 8,
63            Self::Vec3 => 12,
64            Self::Vec4 | Self::Quaternion | Self::Color => 16,
65            Self::String | Self::Bytes | Self::Json | Self::Custom | Self::AssetRef => 32,
66        }
67    }
68
69    pub fn port_color(&self) -> Vec4 {
70        match self {
71            Self::Void => Vec4::new(0.6, 0.6, 0.6, 1.0),
72            Self::Bool => Vec4::new(0.87, 0.4, 0.4, 1.0),
73            Self::Int | Self::UInt => Vec4::new(0.4, 0.8, 0.4, 1.0),
74            Self::Float | Self::Double => Vec4::new(0.4, 0.7, 0.87, 1.0),
75            Self::String | Self::Json => Vec4::new(0.87, 0.7, 0.4, 1.0),
76            Self::Vec2 | Self::Vec3 | Self::Vec4 => Vec4::new(0.6, 0.4, 0.87, 1.0),
77            Self::Quaternion => Vec4::new(0.7, 0.4, 0.87, 1.0),
78            Self::EntityId => Vec4::new(0.87, 0.87, 0.4, 1.0),
79            Self::Color => Vec4::new(0.87, 0.5, 0.3, 1.0),
80            _ => Vec4::new(0.5, 0.5, 0.5, 1.0),
81        }
82    }
83}
84
85// ---------------------------------------------------------------------------
86// Event definition
87// ---------------------------------------------------------------------------
88
89#[derive(Debug, Clone)]
90pub struct EventParam {
91    pub name: String,
92    pub param_type: EventParamType,
93    pub description: String,
94    pub optional: bool,
95    pub default_value: String,
96}
97
98impl EventParam {
99    pub fn new(name: &str, param_type: EventParamType) -> Self {
100        Self {
101            name: name.to_string(),
102            param_type,
103            description: String::new(),
104            optional: false,
105            default_value: String::new(),
106        }
107    }
108    pub fn optional(mut self) -> Self { self.optional = true; self }
109    pub fn with_default(mut self, def: &str) -> Self { self.default_value = def.to_string(); self }
110    pub fn with_description(mut self, desc: &str) -> Self { self.description = desc.to_string(); self }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
114pub enum EventPriority { Lowest = 0, Low = 25, Normal = 50, High = 75, Critical = 100, Immediate = 255 }
115
116impl EventPriority {
117    pub fn label(&self) -> &'static str {
118        match self {
119            Self::Lowest => "Lowest",
120            Self::Low => "Low",
121            Self::Normal => "Normal",
122            Self::High => "High",
123            Self::Critical => "Critical",
124            Self::Immediate => "Immediate",
125        }
126    }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum EventDispatchMode {
131    Immediate,
132    Queued,
133    Deferred,
134    LateTick,
135    FixedUpdate,
136    ThreadSafe,
137    Broadcast,
138}
139
140impl EventDispatchMode {
141    pub fn label(&self) -> &'static str {
142        match self {
143            Self::Immediate => "Immediate",
144            Self::Queued => "Queued",
145            Self::Deferred => "Deferred",
146            Self::LateTick => "Late Tick",
147            Self::FixedUpdate => "Fixed Update",
148            Self::ThreadSafe => "Thread-Safe",
149            Self::Broadcast => "Broadcast",
150        }
151    }
152    pub fn description(&self) -> &'static str {
153        match self {
154            Self::Immediate => "Dispatched and handled in the same call frame",
155            Self::Queued => "Added to a queue, processed next tick",
156            Self::Deferred => "Processed after current frame",
157            Self::LateTick => "Processed at the end of the update loop",
158            Self::FixedUpdate => "Processed during fixed-rate update",
159            Self::ThreadSafe => "Can be raised from any thread",
160            Self::Broadcast => "Sent to all listening systems globally",
161        }
162    }
163}
164
165#[derive(Debug, Clone)]
166pub struct EventDefinition {
167    pub id: u64,
168    pub name: String,
169    pub namespace: String,
170    pub params: Vec<EventParam>,
171    pub priority: EventPriority,
172    pub dispatch_mode: EventDispatchMode,
173    pub category: String,
174    pub description: String,
175    pub cancelable: bool,
176    pub persistent: bool,
177    pub one_shot: bool,
178    pub max_listeners: Option<u32>,
179    pub ttl_ms: Option<u32>,
180    pub tags: Vec<String>,
181    pub deprecated: bool,
182    pub version: u32,
183}
184
185impl EventDefinition {
186    pub fn new(id: u64, name: &str) -> Self {
187        Self {
188            id,
189            name: name.to_string(),
190            namespace: "game".to_string(),
191            params: Vec::new(),
192            priority: EventPriority::Normal,
193            dispatch_mode: EventDispatchMode::Queued,
194            category: "General".to_string(),
195            description: String::new(),
196            cancelable: false,
197            persistent: false,
198            one_shot: false,
199            max_listeners: None,
200            ttl_ms: None,
201            tags: Vec::new(),
202            deprecated: false,
203            version: 1,
204        }
205    }
206    pub fn with_param(mut self, param: EventParam) -> Self { self.params.push(param); self }
207    pub fn with_priority(mut self, priority: EventPriority) -> Self { self.priority = priority; self }
208    pub fn with_mode(mut self, mode: EventDispatchMode) -> Self { self.dispatch_mode = mode; self }
209    pub fn cancelable(mut self) -> Self { self.cancelable = true; self }
210    pub fn persistent(mut self) -> Self { self.persistent = true; self }
211    pub fn with_category(mut self, cat: &str) -> Self { self.category = cat.to_string(); self }
212    pub fn full_name(&self) -> String { format!("{}.{}", self.namespace, self.name) }
213    pub fn payload_size_bytes(&self) -> usize { self.params.iter().map(|p| p.param_type.size_bytes()).sum() }
214}
215
216// ---------------------------------------------------------------------------
217// Event listener
218// ---------------------------------------------------------------------------
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
221pub enum ListenerKind {
222    System,
223    Component,
224    Script,
225    UI,
226    Animation,
227    Audio,
228    Network,
229    AI,
230    Physics,
231    VFX,
232    Custom,
233}
234
235impl ListenerKind {
236    pub fn label(&self) -> &'static str {
237        match self {
238            Self::System => "System",
239            Self::Component => "Component",
240            Self::Script => "Script",
241            Self::UI => "UI",
242            Self::Animation => "Animation",
243            Self::Audio => "Audio",
244            Self::Network => "Network",
245            Self::AI => "AI",
246            Self::Physics => "Physics",
247            Self::VFX => "VFX",
248            Self::Custom => "Custom",
249        }
250    }
251    pub fn color(&self) -> Vec4 {
252        match self {
253            Self::System => Vec4::new(0.8, 0.8, 0.3, 1.0),
254            Self::Component => Vec4::new(0.3, 0.7, 0.9, 1.0),
255            Self::Script => Vec4::new(0.5, 0.9, 0.5, 1.0),
256            Self::UI => Vec4::new(0.9, 0.5, 0.8, 1.0),
257            Self::Animation => Vec4::new(0.9, 0.7, 0.3, 1.0),
258            Self::Audio => Vec4::new(0.5, 0.8, 0.9, 1.0),
259            Self::Network => Vec4::new(0.9, 0.3, 0.3, 1.0),
260            Self::AI => Vec4::new(0.7, 0.4, 0.9, 1.0),
261            Self::Physics => Vec4::new(0.4, 0.9, 0.7, 1.0),
262            Self::VFX => Vec4::new(0.9, 0.6, 0.2, 1.0),
263            Self::Custom => Vec4::new(0.6, 0.6, 0.6, 1.0),
264        }
265    }
266}
267
268#[derive(Debug, Clone)]
269pub struct EventListener {
270    pub id: u64,
271    pub name: String,
272    pub kind: ListenerKind,
273    pub event_id: u64,
274    pub priority: i32,
275    pub consume_event: bool,
276    pub filter_expression: Option<String>,
277    pub handler_code: String,
278    pub enabled: bool,
279    pub call_count: u64,
280    pub total_time_us: f64,
281    pub last_triggered_ms: Option<f64>,
282    pub max_calls_per_frame: Option<u32>,
283    pub cooldown_ms: Option<u32>,
284}
285
286impl EventListener {
287    pub fn new(id: u64, name: &str, event_id: u64, kind: ListenerKind) -> Self {
288        Self {
289            id,
290            name: name.to_string(),
291            kind,
292            event_id,
293            priority: 0,
294            consume_event: false,
295            filter_expression: None,
296            handler_code: String::new(),
297            enabled: true,
298            call_count: 0,
299            total_time_us: 0.0,
300            last_triggered_ms: None,
301            max_calls_per_frame: None,
302            cooldown_ms: None,
303        }
304    }
305    pub fn avg_time_us(&self) -> f64 {
306        if self.call_count == 0 { 0.0 } else { self.total_time_us / self.call_count as f64 }
307    }
308}
309
310// ---------------------------------------------------------------------------
311// Event publisher
312// ---------------------------------------------------------------------------
313
314#[derive(Debug, Clone)]
315pub struct EventPublisher {
316    pub id: u64,
317    pub name: String,
318    pub kind: ListenerKind,
319    pub publishes: Vec<u64>,
320    pub dispatch_count: u64,
321    pub last_dispatch_ms: Option<f64>,
322    pub enabled: bool,
323}
324
325impl EventPublisher {
326    pub fn new(id: u64, name: &str, kind: ListenerKind) -> Self {
327        Self {
328            id,
329            name: name.to_string(),
330            kind,
331            publishes: Vec::new(),
332            dispatch_count: 0,
333            last_dispatch_ms: None,
334            enabled: true,
335        }
336    }
337}
338
339// ---------------------------------------------------------------------------
340// Event channel (direct pub/sub wiring)
341// ---------------------------------------------------------------------------
342
343#[derive(Debug, Clone)]
344pub struct EventChannel {
345    pub id: u64,
346    pub publisher_id: u64,
347    pub listener_id: u64,
348    pub event_id: u64,
349    pub transform_code: Option<String>,
350    pub active: bool,
351    pub total_dispatches: u64,
352    pub dropped_count: u64,
353}
354
355// ---------------------------------------------------------------------------
356// Visual event graph node
357// ---------------------------------------------------------------------------
358
359#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
360pub enum EventNodeKind {
361    EventDefinition,
362    Publisher,
363    Listener,
364    Filter,
365    Transform,
366    Splitter,
367    Merger,
368    Throttle,
369    Debounce,
370    Buffer,
371    Delay,
372    Map,
373    Reduce,
374    Fork,
375    Conditional,
376    Logger,
377    Counter,
378    Timer,
379    Gate,
380    LatchSet,
381    LatchReset,
382    Comment,
383}
384
385impl EventNodeKind {
386    pub fn label(&self) -> &'static str {
387        match self {
388            Self::EventDefinition => "Event",
389            Self::Publisher => "Publisher",
390            Self::Listener => "Listener",
391            Self::Filter => "Filter",
392            Self::Transform => "Transform",
393            Self::Splitter => "Splitter",
394            Self::Merger => "Merger",
395            Self::Throttle => "Throttle",
396            Self::Debounce => "Debounce",
397            Self::Buffer => "Buffer",
398            Self::Delay => "Delay",
399            Self::Map => "Map",
400            Self::Reduce => "Reduce",
401            Self::Fork => "Fork",
402            Self::Conditional => "Conditional",
403            Self::Logger => "Logger",
404            Self::Counter => "Counter",
405            Self::Timer => "Timer",
406            Self::Gate => "Gate",
407            Self::LatchSet => "Latch (Set)",
408            Self::LatchReset => "Latch (Reset)",
409            Self::Comment => "Comment",
410        }
411    }
412    pub fn header_color(&self) -> Vec4 {
413        match self {
414            Self::EventDefinition => Vec4::new(0.2, 0.4, 0.7, 1.0),
415            Self::Publisher => Vec4::new(0.7, 0.4, 0.2, 1.0),
416            Self::Listener => Vec4::new(0.2, 0.7, 0.4, 1.0),
417            Self::Filter | Self::Conditional | Self::Gate => Vec4::new(0.6, 0.2, 0.6, 1.0),
418            Self::Throttle | Self::Debounce | Self::Delay => Vec4::new(0.6, 0.6, 0.2, 1.0),
419            Self::Map | Self::Transform | Self::Reduce => Vec4::new(0.2, 0.6, 0.6, 1.0),
420            Self::Logger | Self::Counter => Vec4::new(0.4, 0.4, 0.4, 1.0),
421            _ => Vec4::new(0.3, 0.3, 0.3, 1.0),
422        }
423    }
424}
425
426static mut EVENT_NODE_ID: u64 = 1;
427fn next_event_node_id() -> u64 {
428    unsafe { let id = EVENT_NODE_ID; EVENT_NODE_ID += 1; id }
429}
430
431#[derive(Debug, Clone)]
432pub struct EventGraphNode {
433    pub id: u64,
434    pub kind: EventNodeKind,
435    pub position: Vec2,
436    pub size: Vec2,
437    pub label: String,
438    pub linked_id: Option<u64>,
439    pub params: HashMap<String, String>,
440    pub enabled: bool,
441    pub collapsed: bool,
442    pub comment: String,
443    pub dispatch_count: u64,
444    pub error_message: Option<String>,
445}
446
447impl EventGraphNode {
448    pub fn new(kind: EventNodeKind, position: Vec2) -> Self {
449        Self {
450            id: next_event_node_id(),
451            kind,
452            position,
453            size: Vec2::new(180.0, 80.0),
454            label: kind.label().to_string(),
455            linked_id: None,
456            params: HashMap::new(),
457            enabled: true,
458            collapsed: false,
459            comment: String::new(),
460            dispatch_count: 0,
461            error_message: None,
462        }
463    }
464    pub fn contains(&self, pt: Vec2) -> bool {
465        pt.x >= self.position.x && pt.x <= self.position.x + self.size.x
466            && pt.y >= self.position.y && pt.y <= self.position.y + self.size.y
467    }
468    pub fn set_param(&mut self, key: &str, value: &str) {
469        self.params.insert(key.to_string(), value.to_string());
470    }
471}
472
473#[derive(Debug, Clone)]
474pub struct EventGraphEdge {
475    pub id: u64,
476    pub from_node: u64,
477    pub from_port: u32,
478    pub to_node: u64,
479    pub to_port: u32,
480    pub event_id: Option<u64>,
481    pub active: bool,
482    pub dispatch_count: u64,
483}
484
485// ---------------------------------------------------------------------------
486// Event graph
487// ---------------------------------------------------------------------------
488
489static mut EVENT_GRAPH_ID: u64 = 1;
490fn next_eg_id() -> u64 { unsafe { let id = EVENT_GRAPH_ID; EVENT_GRAPH_ID += 1; id } }
491
492#[derive(Debug, Clone)]
493pub struct EventGraph {
494    pub name: String,
495    pub nodes: Vec<EventGraphNode>,
496    pub edges: Vec<EventGraphEdge>,
497    pub next_edge_id: u64,
498}
499
500impl EventGraph {
501    pub fn new(name: &str) -> Self {
502        Self { name: name.to_string(), nodes: Vec::new(), edges: Vec::new(), next_edge_id: 1 }
503    }
504
505    pub fn add_node(&mut self, node: EventGraphNode) -> u64 {
506        let id = node.id;
507        self.nodes.push(node);
508        id
509    }
510
511    pub fn connect(&mut self, from: u64, from_port: u32, to: u64, to_port: u32) -> u64 {
512        let id = self.next_edge_id;
513        self.next_edge_id += 1;
514        self.edges.push(EventGraphEdge { id, from_node: from, from_port, to_node: to, to_port, event_id: None, active: true, dispatch_count: 0 });
515        id
516    }
517
518    pub fn find_node(&self, id: u64) -> Option<&EventGraphNode> { self.nodes.iter().find(|n| n.id == id) }
519    pub fn find_node_mut(&mut self, id: u64) -> Option<&mut EventGraphNode> { self.nodes.iter_mut().find(|n| n.id == id) }
520
521    pub fn build_player_combat_graph() -> Self {
522        let mut g = Self::new("PlayerCombat");
523        let fire_event = g.add_node(EventGraphNode::new(EventNodeKind::EventDefinition, Vec2::new(20.0, 20.0)));
524        if let Some(n) = g.find_node_mut(fire_event) { n.label = "OnPlayerFire".to_string(); }
525        let hit_event = g.add_node(EventGraphNode::new(EventNodeKind::EventDefinition, Vec2::new(20.0, 140.0)));
526        if let Some(n) = g.find_node_mut(hit_event) { n.label = "OnHitDetected".to_string(); }
527        let death_event = g.add_node(EventGraphNode::new(EventNodeKind::EventDefinition, Vec2::new(20.0, 260.0)));
528        if let Some(n) = g.find_node_mut(death_event) { n.label = "OnPlayerDeath".to_string(); }
529
530        let fire_pub = g.add_node(EventGraphNode::new(EventNodeKind::Publisher, Vec2::new(260.0, 20.0)));
531        if let Some(n) = g.find_node_mut(fire_pub) { n.label = "WeaponSystem".to_string(); }
532        let hit_pub = g.add_node(EventGraphNode::new(EventNodeKind::Publisher, Vec2::new(260.0, 140.0)));
533        if let Some(n) = g.find_node_mut(hit_pub) { n.label = "HitDetectionSystem".to_string(); }
534
535        let throttle = g.add_node({ let mut n = EventGraphNode::new(EventNodeKind::Throttle, Vec2::new(480.0, 20.0)); n.set_param("rate_hz", "10"); n });
536        let filter = g.add_node({ let mut n = EventGraphNode::new(EventNodeKind::Filter, Vec2::new(480.0, 140.0)); n.set_param("condition", "damage > 0"); n });
537        let splitter = g.add_node(EventGraphNode::new(EventNodeKind::Splitter, Vec2::new(700.0, 140.0)));
538
539        let audio_listener = g.add_node(EventGraphNode::new(EventNodeKind::Listener, Vec2::new(900.0, 80.0)));
540        if let Some(n) = g.find_node_mut(audio_listener) { n.label = "AudioSystem".to_string(); }
541        let vfx_listener = g.add_node(EventGraphNode::new(EventNodeKind::Listener, Vec2::new(900.0, 180.0)));
542        if let Some(n) = g.find_node_mut(vfx_listener) { n.label = "VFXSystem".to_string(); }
543        let ui_listener = g.add_node(EventGraphNode::new(EventNodeKind::Listener, Vec2::new(900.0, 280.0)));
544        if let Some(n) = g.find_node_mut(ui_listener) { n.label = "UIHUDSystem".to_string(); }
545        let logger = g.add_node(EventGraphNode::new(EventNodeKind::Logger, Vec2::new(900.0, 380.0)));
546
547        g.connect(fire_pub, 0, throttle, 0);
548        g.connect(throttle, 0, audio_listener, 0);
549        g.connect(hit_pub, 0, filter, 0);
550        g.connect(filter, 0, splitter, 0);
551        g.connect(splitter, 0, audio_listener, 0);
552        g.connect(splitter, 1, vfx_listener, 0);
553        g.connect(splitter, 2, ui_listener, 0);
554        g.connect(splitter, 3, logger, 0);
555        g
556    }
557}
558
559// ---------------------------------------------------------------------------
560// Event trace (runtime recording)
561// ---------------------------------------------------------------------------
562
563#[derive(Debug, Clone)]
564pub struct EventTraceEntry {
565    pub timestamp_ms: f64,
566    pub frame: u64,
567    pub event_name: String,
568    pub event_id: u64,
569    pub dispatch_mode: EventDispatchMode,
570    pub priority: EventPriority,
571    pub publisher: String,
572    pub listener_count: u32,
573    pub cancelled: bool,
574    pub consumed: bool,
575    pub dispatch_time_us: f32,
576    pub payload_bytes: usize,
577}
578
579#[derive(Debug, Clone, Default)]
580pub struct EventTrace {
581    pub entries: Vec<EventTraceEntry>,
582    pub recording: bool,
583    pub max_entries: usize,
584    pub filter_event: Option<String>,
585    pub filter_publisher: Option<String>,
586    pub paused: bool,
587}
588
589impl EventTrace {
590    pub fn new(max: usize) -> Self { Self { max_entries: max, ..Default::default() } }
591    pub fn push(&mut self, entry: EventTraceEntry) {
592        if !self.recording || self.paused { return; }
593        if let Some(ref f) = self.filter_event {
594            if !entry.event_name.contains(f.as_str()) { return; }
595        }
596        self.entries.push(entry);
597        if self.entries.len() > self.max_entries { self.entries.remove(0); }
598    }
599    pub fn clear(&mut self) { self.entries.clear(); }
600    pub fn total_events(&self) -> usize { self.entries.len() }
601    pub fn events_per_second(&self) -> f64 {
602        if self.entries.len() < 2 { return 0.0; }
603        let dt = self.entries.last().unwrap().timestamp_ms - self.entries.first().unwrap().timestamp_ms;
604        if dt <= 0.0 { return 0.0; }
605        self.entries.len() as f64 / (dt / 1000.0)
606    }
607    pub fn most_frequent(&self, n: usize) -> Vec<(&str, usize)> {
608        let mut counts: HashMap<&str, usize> = HashMap::new();
609        for e in &self.entries { *counts.entry(e.event_name.as_str()).or_insert(0) += 1; }
610        let mut sorted: Vec<(&str, usize)> = counts.into_iter().collect();
611        sorted.sort_by(|a, b| b.1.cmp(&a.1));
612        sorted.into_iter().take(n).collect()
613    }
614    pub fn avg_dispatch_time_us(&self) -> f32 {
615        if self.entries.is_empty() { return 0.0; }
616        self.entries.iter().map(|e| e.dispatch_time_us).sum::<f32>() / self.entries.len() as f32
617    }
618    pub fn cancelled_count(&self) -> usize { self.entries.iter().filter(|e| e.cancelled).count() }
619}
620
621// ---------------------------------------------------------------------------
622// Event queue visualization
623// ---------------------------------------------------------------------------
624
625#[derive(Debug, Clone)]
626pub struct QueuedEvent {
627    pub event_id: u64,
628    pub event_name: String,
629    pub queued_at_ms: f64,
630    pub scheduled_frame: u64,
631    pub priority: EventPriority,
632    pub publisher: String,
633    pub payload_preview: String,
634    pub age_ms: f32,
635}
636
637#[derive(Debug, Clone, Default)]
638pub struct EventQueueVisualizer {
639    pub queued: Vec<QueuedEvent>,
640    pub processed_this_frame: u32,
641    pub dropped_this_frame: u32,
642    pub max_queue_depth_seen: usize,
643    pub avg_queue_depth: f32,
644    pub frame: u64,
645}
646
647impl EventQueueVisualizer {
648    pub fn simulate_frame(&mut self, current_time_ms: f64) {
649        self.frame += 1;
650        // simulate random events being added and processed
651        let seed: u64 = self.frame.wrapping_mul(6364136223846793005);
652        let rng = ((seed >> 33) as f32) / (u32::MAX as f32);
653
654        if rng > 0.3 {
655            self.queued.push(QueuedEvent {
656                event_id: self.frame,
657                event_name: ["OnPlayerMove", "OnHit", "OnFire", "OnCollision", "OnTick"][((rng * 5.0) as usize).min(4)].to_string(),
658                queued_at_ms: current_time_ms,
659                scheduled_frame: self.frame,
660                priority: EventPriority::Normal,
661                publisher: "GameSystem".to_string(),
662                payload_preview: "{ ... }".to_string(),
663                age_ms: 0.0,
664            });
665        }
666
667        let to_process = (self.queued.len() as f32 * 0.8) as usize;
668        self.processed_this_frame = to_process as u32;
669        self.queued = self.queued.split_off(to_process.min(self.queued.len()));
670
671        if self.queued.len() > self.max_queue_depth_seen {
672            self.max_queue_depth_seen = self.queued.len();
673        }
674        self.avg_queue_depth = self.avg_queue_depth * 0.95 + self.queued.len() as f32 * 0.05;
675    }
676}
677
678// ---------------------------------------------------------------------------
679// Event library
680// ---------------------------------------------------------------------------
681
682#[derive(Debug, Clone, Default)]
683pub struct EventLibrary {
684    pub events: Vec<EventDefinition>,
685    pub next_id: u64,
686}
687
688impl EventLibrary {
689    pub fn with_defaults() -> Self {
690        let mut lib = Self { events: Vec::new(), next_id: 1 };
691        lib.add(EventDefinition::new(lib.next_id, "OnGameStart").with_category("Game"));
692        lib.next_id += 1;
693        lib.add(EventDefinition::new(lib.next_id, "OnGameEnd").with_category("Game")
694            .with_param(EventParam::new("reason", EventParamType::String)));
695        lib.next_id += 1;
696        lib.add(EventDefinition::new(lib.next_id, "OnLevelLoaded").with_category("Level")
697            .with_param(EventParam::new("scene_name", EventParamType::String)));
698        lib.next_id += 1;
699        lib.add(EventDefinition::new(lib.next_id, "OnPlayerSpawned").with_category("Player")
700            .with_param(EventParam::new("entity_id", EventParamType::EntityId))
701            .with_param(EventParam::new("position", EventParamType::Vec3)));
702        lib.next_id += 1;
703        lib.add(EventDefinition::new(lib.next_id, "OnPlayerDied").with_category("Player")
704            .with_param(EventParam::new("entity_id", EventParamType::EntityId))
705            .with_param(EventParam::new("killer_id", EventParamType::EntityId).optional())
706            .with_priority(EventPriority::High).cancelable());
707        lib.next_id += 1;
708        lib.add(EventDefinition::new(lib.next_id, "OnDamageDealt").with_category("Combat")
709            .with_param(EventParam::new("target", EventParamType::EntityId))
710            .with_param(EventParam::new("attacker", EventParamType::EntityId))
711            .with_param(EventParam::new("amount", EventParamType::Float))
712            .with_param(EventParam::new("type", EventParamType::String))
713            .cancelable());
714        lib.next_id += 1;
715        lib.add(EventDefinition::new(lib.next_id, "OnItemPickedUp").with_category("Inventory")
716            .with_param(EventParam::new("entity_id", EventParamType::EntityId))
717            .with_param(EventParam::new("item_id", EventParamType::AssetRef))
718            .with_param(EventParam::new("count", EventParamType::Int)));
719        lib.next_id += 1;
720        lib.add(EventDefinition::new(lib.next_id, "OnQuestCompleted").with_category("Quest")
721            .with_param(EventParam::new("quest_id", EventParamType::String))
722            .with_priority(EventPriority::High));
723        lib.next_id += 1;
724        lib.add(EventDefinition::new(lib.next_id, "OnDialogueStarted").with_category("Dialogue")
725            .with_param(EventParam::new("npc_id", EventParamType::EntityId))
726            .with_param(EventParam::new("dialogue_id", EventParamType::String)));
727        lib.next_id += 1;
728        lib.add(EventDefinition::new(lib.next_id, "OnAchievementUnlocked").with_category("Progression")
729            .with_param(EventParam::new("achievement_id", EventParamType::String))
730            .with_priority(EventPriority::High).persistent());
731        lib.next_id += 1;
732        lib.add(EventDefinition::new(lib.next_id, "OnUIButtonClicked").with_category("UI")
733            .with_param(EventParam::new("button_id", EventParamType::String))
734            .with_mode(EventDispatchMode::Immediate));
735        lib.next_id += 1;
736        lib.add(EventDefinition::new(lib.next_id, "OnSettingsChanged").with_category("System")
737            .with_param(EventParam::new("category", EventParamType::String))
738            .with_mode(EventDispatchMode::Broadcast));
739        lib.next_id += 1;
740        lib.add(EventDefinition::new(lib.next_id, "OnNetworkDisconnect").with_category("Network")
741            .with_param(EventParam::new("player_id", EventParamType::EntityId))
742            .with_param(EventParam::new("reason", EventParamType::String))
743            .with_priority(EventPriority::Critical));
744        lib.next_id += 1;
745        lib.add(EventDefinition::new(lib.next_id, "OnPhysicsCollision").with_category("Physics")
746            .with_param(EventParam::new("entity_a", EventParamType::EntityId))
747            .with_param(EventParam::new("entity_b", EventParamType::EntityId))
748            .with_param(EventParam::new("contact_point", EventParamType::Vec3))
749            .with_param(EventParam::new("impulse", EventParamType::Float))
750            .with_mode(EventDispatchMode::FixedUpdate));
751        lib.next_id += 1;
752        lib.add(EventDefinition::new(lib.next_id, "OnAnimationEvent").with_category("Animation")
753            .with_param(EventParam::new("entity_id", EventParamType::EntityId))
754            .with_param(EventParam::new("event_name", EventParamType::String))
755            .with_param(EventParam::new("frame", EventParamType::Int)));
756        lib.next_id += 1;
757        lib
758    }
759
760    pub fn add(&mut self, def: EventDefinition) { self.events.push(def); }
761    pub fn find_by_name(&self, name: &str) -> Option<&EventDefinition> { self.events.iter().find(|e| e.name == name) }
762    pub fn find_by_id(&self, id: u64) -> Option<&EventDefinition> { self.events.iter().find(|e| e.id == id) }
763    pub fn by_category(&self) -> HashMap<&str, Vec<&EventDefinition>> {
764        let mut map: HashMap<&str, Vec<&EventDefinition>> = HashMap::new();
765        for e in &self.events { map.entry(e.category.as_str()).or_default().push(e); }
766        map
767    }
768    pub fn search(&self, query: &str) -> Vec<&EventDefinition> {
769        let q = query.to_lowercase();
770        self.events.iter().filter(|e| e.name.to_lowercase().contains(&q) || e.category.to_lowercase().contains(&q)).collect()
771    }
772    pub fn total_payload_size_bytes(&self) -> usize { self.events.iter().map(|e| e.payload_size_bytes()).sum() }
773}
774
775// ---------------------------------------------------------------------------
776// Event editor panels
777// ---------------------------------------------------------------------------
778
779#[derive(Debug, Clone, Copy, PartialEq)]
780pub enum EventEditorPanel {
781    EventLibrary,
782    Graph,
783    Listeners,
784    Publishers,
785    Trace,
786    Queue,
787    Statistics,
788    CodeGen,
789}
790
791impl EventEditorPanel {
792    pub fn label(&self) -> &'static str {
793        match self {
794            Self::EventLibrary => "Events",
795            Self::Graph => "Graph",
796            Self::Listeners => "Listeners",
797            Self::Publishers => "Publishers",
798            Self::Trace => "Trace",
799            Self::Queue => "Queue",
800            Self::Statistics => "Statistics",
801            Self::CodeGen => "Code Gen",
802        }
803    }
804}
805
806// ---------------------------------------------------------------------------
807// Statistics
808// ---------------------------------------------------------------------------
809
810#[derive(Debug, Clone, Default)]
811pub struct EventStatistics {
812    pub total_dispatched: u64,
813    pub total_cancelled: u64,
814    pub total_consumed: u64,
815    pub total_dropped: u64,
816    pub per_event_counts: HashMap<String, u64>,
817    pub per_publisher_counts: HashMap<String, u64>,
818    pub per_listener_counts: HashMap<String, u64>,
819    pub peak_events_per_frame: u32,
820    pub avg_events_per_frame: f32,
821    pub avg_dispatch_time_us: f32,
822    pub peak_queue_depth: usize,
823    pub hottest_event: Option<String>,
824}
825
826impl EventStatistics {
827    pub fn record_dispatch(&mut self, event: &str, publisher: &str, cancelled: bool, consumed: bool) {
828        self.total_dispatched += 1;
829        if cancelled { self.total_cancelled += 1; }
830        if consumed { self.total_consumed += 1; }
831        *self.per_event_counts.entry(event.to_string()).or_insert(0) += 1;
832        *self.per_publisher_counts.entry(publisher.to_string()).or_insert(0) += 1;
833        self.hottest_event = self.per_event_counts.iter()
834            .max_by_key(|(_, &v)| v)
835            .map(|(k, _)| k.clone());
836    }
837    pub fn top_events(&self, n: usize) -> Vec<(&String, &u64)> {
838        let mut sorted: Vec<(&String, &u64)> = self.per_event_counts.iter().collect();
839        sorted.sort_by(|a, b| b.1.cmp(a.1));
840        sorted.into_iter().take(n).collect()
841    }
842    pub fn cancel_rate(&self) -> f32 {
843        if self.total_dispatched == 0 { return 0.0; }
844        self.total_cancelled as f32 / self.total_dispatched as f32
845    }
846}
847
848// ---------------------------------------------------------------------------
849// Code generation
850// ---------------------------------------------------------------------------
851
852#[derive(Debug, Clone, Copy, PartialEq)]
853pub enum CodeGenLanguage { Rust, Cpp, CSharp, TypeScript, Python, Lua }
854
855impl CodeGenLanguage {
856    pub fn label(&self) -> &'static str {
857        match self {
858            Self::Rust => "Rust", Self::Cpp => "C++", Self::CSharp => "C#",
859            Self::TypeScript => "TypeScript", Self::Python => "Python", Self::Lua => "Lua",
860        }
861    }
862}
863
864fn generate_rust_event_code(lib: &EventLibrary) -> String {
865    let mut lines = vec![
866        "// Auto-generated event types".to_string(),
867        "use std::any::Any;".to_string(),
868        "".to_string(),
869        "pub trait Event: Any { fn name(&self) -> &'static str; }".to_string(),
870        "".to_string(),
871    ];
872    for def in &lib.events {
873        lines.push(format!("/// {}", def.description));
874        lines.push(format!("pub struct {} {{", def.name));
875        for param in &def.params {
876            let rust_type = match param.param_type {
877                EventParamType::Bool => "bool",
878                EventParamType::Int => "i32",
879                EventParamType::UInt => "u32",
880                EventParamType::Float => "f32",
881                EventParamType::Double => "f64",
882                EventParamType::String => "String",
883                EventParamType::Vec2 => "glam::Vec2",
884                EventParamType::Vec3 => "glam::Vec3",
885                EventParamType::Vec4 => "glam::Vec4",
886                EventParamType::Quaternion => "glam::Quat",
887                EventParamType::EntityId => "u64",
888                EventParamType::AssetRef => "String",
889                EventParamType::Color => "glam::Vec4",
890                _ => "Vec<u8>",
891            };
892            let opt = if param.optional { format!("Option<{}>", rust_type) } else { rust_type.to_string() };
893            lines.push(format!("    pub {}: {},", param.name, opt));
894        }
895        lines.push("}".to_string());
896        lines.push(format!("impl Event for {} {{ fn name(&self) -> &'static str {{ \"{}\" }} }}", def.name, def.full_name()));
897        lines.push("".to_string());
898    }
899    lines.join("\n")
900}
901
902fn generate_cpp_event_code(lib: &EventLibrary) -> String {
903    let mut lines = vec![
904        "// Auto-generated event types".to_string(),
905        "#pragma once".to_string(),
906        "#include <string>".to_string(),
907        "#include <glm/glm.hpp>".to_string(),
908        "".to_string(),
909    ];
910    for def in &lib.events {
911        lines.push(format!("struct {} {{", def.name));
912        for param in &def.params {
913            let cpp_type = match param.param_type {
914                EventParamType::Bool => "bool",
915                EventParamType::Int => "int32_t",
916                EventParamType::UInt => "uint32_t",
917                EventParamType::Float => "float",
918                EventParamType::Double => "double",
919                EventParamType::String => "std::string",
920                EventParamType::Vec2 => "glm::vec2",
921                EventParamType::Vec3 => "glm::vec3",
922                EventParamType::Vec4 => "glm::vec4",
923                EventParamType::Quaternion => "glm::quat",
924                EventParamType::EntityId => "uint64_t",
925                _ => "std::vector<uint8_t>",
926            };
927            lines.push(format!("    {} {};", cpp_type, param.name));
928        }
929        lines.push(format!("    static constexpr const char* NAME = \"{}\";", def.full_name()));
930        lines.push("};".to_string());
931        lines.push("".to_string());
932    }
933    lines.join("\n")
934}
935
936// ---------------------------------------------------------------------------
937// Main event editor
938// ---------------------------------------------------------------------------
939
940#[derive(Debug)]
941pub struct EventSystemEditor {
942    pub library: EventLibrary,
943    pub graphs: Vec<EventGraph>,
944    pub active_graph: usize,
945    pub listeners: Vec<EventListener>,
946    pub publishers: Vec<EventPublisher>,
947    pub channels: Vec<EventChannel>,
948    pub trace: EventTrace,
949    pub queue_vis: EventQueueVisualizer,
950    pub statistics: EventStatistics,
951    pub active_panel: EventEditorPanel,
952    pub selected_event: Option<u64>,
953    pub selected_node: Option<u64>,
954    pub selected_listener: Option<usize>,
955    pub selected_publisher: Option<usize>,
956    pub search_query: String,
957    pub category_filter: Option<String>,
958    pub codegen_language: CodeGenLanguage,
959    pub generated_code: String,
960    pub zoom: f32,
961    pub pan: Vec2,
962    pub simulation_time_ms: f64,
963    pub auto_trace: bool,
964    pub trace_max_entries: usize,
965    pub show_cancelled: bool,
966    pub show_consumed: bool,
967    pub history: Vec<Vec<EventGraphNode>>,
968    pub history_pos: usize,
969}
970
971impl Default for EventSystemEditor {
972    fn default() -> Self {
973        let library = EventLibrary::with_defaults();
974        let graphs = vec![
975            EventGraph::build_player_combat_graph(),
976            EventGraph::new("UIEvents"),
977            EventGraph::new("NetworkEvents"),
978        ];
979        let mut trace = EventTrace::new(2048);
980        trace.recording = true;
981
982        // Synthetic listeners
983        let listeners = vec![
984            EventListener::new(1, "AudioSystem.OnHit", 6, ListenerKind::Audio),
985            EventListener::new(2, "VFXSystem.OnHit", 6, ListenerKind::VFX),
986            EventListener::new(3, "UIHud.OnDamage", 6, ListenerKind::UI),
987            EventListener::new(4, "QuestTracker.OnKill", 5, ListenerKind::System),
988            EventListener::new(5, "AchievementSystem.OnDeath", 5, ListenerKind::System),
989        ];
990
991        let publishers = vec![
992            EventPublisher::new(1, "WeaponSystem", ListenerKind::System),
993            EventPublisher::new(2, "HealthSystem", ListenerKind::System),
994            EventPublisher::new(3, "PlayerController", ListenerKind::Component),
995            EventPublisher::new(4, "PhysicsSystem", ListenerKind::Physics),
996        ];
997
998        Self {
999            library,
1000            graphs,
1001            active_graph: 0,
1002            listeners,
1003            publishers,
1004            channels: Vec::new(),
1005            trace,
1006            queue_vis: EventQueueVisualizer::default(),
1007            statistics: EventStatistics::default(),
1008            active_panel: EventEditorPanel::EventLibrary,
1009            selected_event: None,
1010            selected_node: None,
1011            selected_listener: None,
1012            selected_publisher: None,
1013            search_query: String::new(),
1014            category_filter: None,
1015            codegen_language: CodeGenLanguage::Rust,
1016            generated_code: String::new(),
1017            zoom: 1.0,
1018            pan: Vec2::ZERO,
1019            simulation_time_ms: 0.0,
1020            auto_trace: true,
1021            trace_max_entries: 2048,
1022            show_cancelled: true,
1023            show_consumed: true,
1024            history: Vec::new(),
1025            history_pos: 0,
1026        }
1027    }
1028}
1029
1030impl EventSystemEditor {
1031    pub fn active_graph(&self) -> &EventGraph { &self.graphs[self.active_graph] }
1032    pub fn active_graph_mut(&mut self) -> &mut EventGraph { &mut self.graphs[self.active_graph] }
1033
1034    pub fn simulate_tick(&mut self, dt_ms: f64) {
1035        self.simulation_time_ms += dt_ms;
1036        self.queue_vis.simulate_frame(self.simulation_time_ms);
1037
1038        // Inject synthetic trace events
1039        if self.auto_trace && (self.simulation_time_ms as u64 % 16) == 0 {
1040            let events = ["OnHit", "OnPlayerMove", "OnFire", "OnTick", "OnCollision"];
1041            let idx = (self.simulation_time_ms as usize / 16) % events.len();
1042            self.trace.push(EventTraceEntry {
1043                timestamp_ms: self.simulation_time_ms,
1044                frame: self.queue_vis.frame,
1045                event_name: events[idx].to_string(),
1046                event_id: idx as u64 + 1,
1047                dispatch_mode: EventDispatchMode::Queued,
1048                priority: EventPriority::Normal,
1049                publisher: "GameSystem".to_string(),
1050                listener_count: 2,
1051                cancelled: false,
1052                consumed: false,
1053                dispatch_time_us: 0.5,
1054                payload_bytes: 16,
1055            });
1056            self.statistics.record_dispatch(events[idx], "GameSystem", false, false);
1057        }
1058    }
1059
1060    pub fn generate_code(&mut self) {
1061        self.generated_code = match self.codegen_language {
1062            CodeGenLanguage::Rust => generate_rust_event_code(&self.library),
1063            CodeGenLanguage::Cpp => generate_cpp_event_code(&self.library),
1064            _ => "// Code generation not yet supported for this language".to_string(),
1065        };
1066    }
1067
1068    pub fn filtered_events(&self) -> Vec<&EventDefinition> {
1069        let q = self.search_query.to_lowercase();
1070        self.library.events.iter().filter(|e| {
1071            let cat_ok = self.category_filter.as_ref().map_or(true, |f| &e.category == f);
1072            let text_ok = q.is_empty() || e.name.to_lowercase().contains(&q) || e.category.to_lowercase().contains(&q);
1073            cat_ok && text_ok
1074        }).collect()
1075    }
1076
1077    pub fn listeners_for_event(&self, event_id: u64) -> Vec<&EventListener> {
1078        self.listeners.iter().filter(|l| l.event_id == event_id).collect()
1079    }
1080
1081    pub fn snapshot(&mut self) {
1082        let nodes = self.active_graph().nodes.clone();
1083        self.history.truncate(self.history_pos);
1084        self.history.push(nodes);
1085        self.history_pos = self.history.len();
1086    }
1087
1088    pub fn undo(&mut self) {
1089        if self.history_pos > 1 {
1090            self.history_pos -= 1;
1091            let nodes = self.history[self.history_pos - 1].clone();
1092            self.active_graph_mut().nodes = nodes;
1093        }
1094    }
1095
1096    pub fn redo(&mut self) {
1097        if self.history_pos < self.history.len() {
1098            let nodes = self.history[self.history_pos].clone();
1099            self.active_graph_mut().nodes = nodes;
1100            self.history_pos += 1;
1101        }
1102    }
1103}