/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of OmiLAXR. */ using System.Collections.Generic; using System.Linq; using OmiLAXR.Composers; using OmiLAXR.Composers.HigherComposers; using OmiLAXR.Endpoints; using OmiLAXR.Hooks; using UnityEngine; namespace OmiLAXR { /// /// Core component that manages data flow through the OmiLAXR pipeline system. /// Serves as a central hub for composers, hooks, and endpoints. /// Executes early in Unity's order to ensure data is available for other components. /// [AddComponentMenu("OmiLAXR / Core / Data Provider")] [DefaultExecutionOrder(-1)] public class DataProvider : PipelineComponent { /// /// Collection of composers that generate statements/data for the pipeline. /// public readonly List Composers = new List(); /// /// Collection of higher-level composers that process and aggregate statements /// from regular composers to create more complex data structures. /// public readonly List> HigherComposers = new List>(); /// /// Collection of hooks that can intercept and modify statements as they flow through the pipeline. /// public readonly List Hooks = new List(); /// /// Collection of endpoints that receive and process the final statements, /// often responsible for data persistence, transmission, or visualization. /// public readonly List Endpoints = new List(); /// /// Extensions that can add functionality to the DataProvider without modifying its core implementation. /// public readonly List Extensions = new List(); /// /// Retrieves the first composer of the specified type. /// /// Type of composer to retrieve /// The first composer of the specified type, or null if none exists public T GetComposer() where T : IComposer => Composers.OfType().FirstOrDefault(); private bool _isInit; /// /// Initializes the DataProvider by discovering and registering all available /// composers, higher composers, hooks, and endpoints in its children. /// Sets up event subscriptions for processing statements. /// protected override void OnEnable() { if (_isInit) return; // Find available composers var composers = GetComponentsInChildren(true); Composers.AddRange(composers); // Find available higher composers HigherComposers.AddRange(Composers.Where(c => c.IsHigherComposer) .Select(c => c as HigherComposer)); // Find available hooks Hooks.AddRange(GetComponentsInChildren(true)); // Find available data endpoints Endpoints.AddRange(GetComponentsInChildren(true)); // Subscribe to each composer's AfterComposed event to process statements foreach (var composer in Composers) { composer.AfterComposed += HandleStatement; } _isInit = true; } private void OnDisable() { Cleanup(); } private void Cleanup() { if (!_isInit) return; foreach (var composer in Composers) { composer.AfterComposed -= HandleStatement; } Composers.Clear(); Endpoints.Clear(); HigherComposers.Clear(); Hooks.Clear(); _isInit = false; } /// /// Processes statements from composers through the pipeline flow: /// 1. Provides statements to higher composers for potential aggregation /// 2. Passes statements through all active hooks for modification/filtering /// 3. Distributes statements to all registered endpoints /// /// The composer that generated the statement /// The data/statement to be processed private void HandleStatement(IComposer sender, IStatement statement) { // First, allow higher composers to examine and potentially aggregate the statement foreach (var composer in HigherComposers) { if (!composer.enabled) continue; composer.LookFor(statement); } // Then, pass through hooks for potential modification or filtering foreach (var hook in Hooks) { if (!hook.enabled) continue; statement = hook.AfterCompose(statement); if (statement.IsDiscarded()) return; // Statement was marked to be discarded by a hook } // Finally, distribute to all endpoints foreach (var dp in Endpoints) { if (!dp.enabled) continue; dp.SendStatement(statement); } } /// /// Static helper to find the DataProvider instance in the scene. /// /// The first available DataProvider public static DataProvider GetAll() => FindObject(); } }