#pragma once #include "core/object/class_db.h" #include "core/object/object.h" #include "core/os/mutex.h" #include "core/string/ustring.h" #include "core/variant/dictionary.h" #include "core/variant/array.h" // PostHog-like analytics + feature flags, built into the engine. // Self-hosted: events stay local (JSONL) or are forwarded to a configured endpoint. class AgentAnalytics : public Object { GDCLASS(AgentAnalytics, Object); static AgentAnalytics *singleton; public: static AgentAnalytics *get_singleton() { return singleton; } struct AnalyticsEvent { uint64_t id = 0; uint64_t timestamp_msec = 0; String event_name; String distinct_id; // User/session/agent ID. Dictionary properties; }; struct FeatureFlag { String key; bool enabled = false; Variant value; // Can be bool, string, int, etc. Dictionary conditions; // Targeting rules. float rollout_percentage = 100.0f; String description; }; struct Funnel { String name; Array steps; // Array of event names. Dictionary step_counts; // step_name -> count. }; private: // Event storage. static constexpr int EVENT_BUFFER_SIZE = 50000; Vector events; uint64_t next_event_id = 1; Mutex events_mutex; // Feature flags. HashMap feature_flags; Mutex flags_mutex; // Session tracking. String current_session_id; String current_distinct_id; uint64_t session_start = 0; // Funnels. HashMap funnels; // File sink for event persistence. bool file_sink_enabled = false; String file_sink_path; // Remote endpoint (optional — send events to external analytics). String remote_endpoint; String remote_api_key; bool remote_enabled = false; // A/B test groups. HashMap experiment_groups; // experiment_key -> group_name void _write_event_to_file(const AnalyticsEvent &p_event); void _send_event_to_remote(const AnalyticsEvent &p_event); bool _evaluate_flag_conditions(const FeatureFlag &p_flag, const Dictionary &p_context); String _generate_session_id(); protected: static void _bind_methods(); public: AgentAnalytics(); ~AgentAnalytics(); // --- Event Tracking (PostHog capture equivalent) --- void capture(const String &p_event, const Dictionary &p_properties = Dictionary()); void identify(const String &p_distinct_id, const Dictionary &p_properties = Dictionary()); void alias(const String &p_alias, const String &p_distinct_id); // Session management. void start_session(const String &p_distinct_id = ""); void end_session(); String get_session_id() const { return current_session_id; } // Page/screen views (game equivalent: level/scene tracking). void screen(const String &p_screen_name, const Dictionary &p_properties = Dictionary()); // --- Feature Flags --- bool is_feature_enabled(const String &p_key, const Dictionary &p_context = Dictionary()); Variant get_feature_flag(const String &p_key, const Variant &p_default = Variant()); void set_feature_flag(const String &p_key, bool p_enabled, const Variant &p_value = Variant(), float p_rollout = 100.0f, const String &p_description = ""); void remove_feature_flag(const String &p_key); Array get_all_flags() const; bool load_flags_from_file(const String &p_path = "res://feature_flags.json"); bool save_flags_to_file(const String &p_path = "res://feature_flags.json"); // --- A/B Testing --- String get_experiment_group(const String &p_experiment_key, const Array &p_groups); Dictionary get_experiment_results(const String &p_experiment_key); // --- Funnels --- void define_funnel(const String &p_name, const Array &p_steps); Dictionary get_funnel_stats(const String &p_name); // --- Query --- Array get_events(int p_count = 100, const String &p_event_name = "", uint64_t p_since_msec = 0); int get_event_count(const String &p_event_name = ""); Dictionary get_event_counts_by_name(); // {event_name: count} // --- Configuration --- void enable_file_sink(const String &p_path = "user://analytics.jsonl"); void disable_file_sink(); void set_remote_endpoint(const String &p_endpoint, const String &p_api_key); void disable_remote(); void set_distinct_id(const String &p_id) { current_distinct_id = p_id; } String get_distinct_id() const { return current_distinct_id; } // --- Super Properties (attached to every event) --- Dictionary super_properties; void set_super_property(const String &p_key, const Variant &p_value); void remove_super_property(const String &p_key); void clear_super_properties(); void clear(); };