#ifndef SCENE_SYNCHRONIZER_DEBUGGER_H #define SCENE_SYNCHRONIZER_DEBUGGER_H /*************************************************************************/ /* scene_synchronizer_debugger.h */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "scene/main/node.h" #ifdef DEBUG_ENABLED #include "core/containers/oa_hash_map.h" #endif class SceneSynchronizer; class SceneTree; class SceneSynchronizerDebugger : public Node { GDCLASS(SceneSynchronizerDebugger, Node); static SceneSynchronizerDebugger *the_singleton; public: struct TrackedNode { Node *node; List *properties; TrackedNode() = default; TrackedNode(Node *p_node) : node(p_node) {} TrackedNode(Node *p_node, List *p_properties) : node(p_node), properties(p_properties) {} bool operator==(const TrackedNode &p_t) const { return p_t.node == node; } }; enum DataBufferDumpMode { NONE, WRITE, READ }; enum FrameEvent : uint32_t { EMPTY = 0, CLIENT_DESYNC_DETECTED = 1 << 0, CLIENT_DESYNC_DETECTED_SOFT = 1 << 0, }; public: static SceneSynchronizerDebugger *singleton(); static void _bind_methods(); private: #ifdef DEBUG_ENABLED bool dump_enabled = false; LocalVector dump_classes; bool setup_done = false; uint32_t log_counter = 0; SceneTree *scene_tree = nullptr; String main_dump_directory_path; String dump_name; LocalVector tracked_nodes; // HaskMap between class name and property list: to avoid fetching the property list per object each frame. OAHashMap> classes_property_lists; // Dictionary of dictionary containing nodes info. Dictionary frame_dump__begin_state; // Dictionary of dictionary containing nodes info. Dictionary frame_dump__end_state; // The dictionary containing the data buffer operations performed by the controllers. Dictionary frame_dump__node_log; // The controller name for which the data buffer operations is in progress. String frame_dump__data_buffer_name; // A really small description about what happens on this frame. FrameEvent frame_dump__frame_events = FrameEvent::EMPTY; // This Array contains all the inputs (stringified) written on the `DataBuffer` from the // `_controller_process` function Array frame_dump__data_buffer_writes; // This Array contains all the inputs (stringified) read on the `DataBuffer` from the // `_controller_process` function Array frame_dump__data_buffer_reads; // This Dictionary contains the comparison (`_are_inputs_different`) fetched by this frame, and // the result. Dictionary frame_dump__are_inputs_different_results; DataBufferDumpMode frame_dump_data_buffer_dump_mode = NONE; bool frame_dump__has_warnings = false; bool frame_dump__has_errors = false; #endif public: SceneSynchronizerDebugger(); ~SceneSynchronizerDebugger(); void set_dump_enabled(bool p_dump_enabled); bool get_dump_enabled() const; void register_class_for_node_to_dump(Node *p_node); void register_class_to_dump(const StringName &p_class); void unregister_class_to_dump(const StringName &p_class); void setup_debugger(const String &p_dump_name, int p_peer, SceneTree *p_scene_tree); private: void prepare_dumping(int p_peer, SceneTree *p_scene_tree); void setup_debugger_python_ui(); void track_node(Node *p_node, bool p_recursive); void on_node_added(Node *p_node); void on_node_removed(Node *p_node); public: void write_dump(int p_peer, uint32_t p_frame_index); void start_new_frame(); void scene_sync_process_start(const SceneSynchronizer *p_scene_sync); void scene_sync_process_end(const SceneSynchronizer *p_scene_sync); void databuffer_operation_begin_record(Node *p_node, DataBufferDumpMode p_mode); void databuffer_operation_end_record(); void databuffer_write(uint32_t p_data_type, uint32_t p_compression_level, Variant p_variable); void databuffer_read(uint32_t p_data_type, uint32_t p_compression_level, Variant p_variable); void notify_input_sent_to_server(Node *p_node, uint32_t p_frame_index, uint32_t p_input_index); void notify_are_inputs_different_result(Node *p_node, uint32_t p_other_frame_index, bool p_is_similar); void add_node_message(Node *p_node, const String &p_message); void add_node_message_by_path(const String &p_node_path, const String &p_message); void debug_print(Node *p_node, const String &p_message, bool p_silent = false); void debug_warning(Node *p_node, const String &p_message, bool p_silent = false); void debug_error(Node *p_node, const String &p_message, bool p_silent = false); void notify_event(FrameEvent p_event); private: void dump_tracked_objects(const SceneSynchronizer *p_scene_sync, Dictionary &p_dump); }; #endif