Amerio.Stephane Posted July 21, 2025 Posted July 21, 2025 Hello, I'm trying to adapt our components so they can be more easily configured from inside the editor. I having quite a few successes, but some of the ig_aviation for example plugins are not compatible with this way of doing. As a matter of fact, the IG plugin is not available inside the Editor, so typically all components that rely on it should first check for this condition before continuing. But as an obvious example, the macro IG_ONLY_FOR_MASTER is clearly not Editor-proof: #define IG_ONLY_FOR_MASTER \ if (!Unigine::Plugins::IG::Manager::get()->isMaster()) \ return; This leads to an immediate crash in the Editor, of course. And I suspect more of them have some sort of unforeseen incompatibilities. To be clear: I can understand that a runtime-only, IG specific plugin, is not meant to work nor be editable/configurable from within the editor (that is a dedicated work in itself), but as it is, they prevent us completely from making any editor-compatible component. Could there be any way to prevent some components to be evaluated in the Editor, while some others keep be evaluated? Could you make some basic pre-conditions test in the problematic components to just bypass the init/update/shutdown of these? To give more context, we have some components that rely on hard-to-guess float values or that would play some animation when triggered. Without in-editor execution, we have to tweak the values/save/relaunch the app/do it again a few dozen times before getting it right. Tedious. Side note: We define some Console variables in the cpp of the components, for example to trigger some debug visualizer info. But these variables are not reachable from the Editor console, although the component itself does access them correctly. Is it a bug, or should we doing something specific to have them accessible in the editor?
cash-metall Posted July 23, 2025 Posted July 23, 2025 Hello! Yes, indeed, the IG_ONLY_FOR_MASTER macro does not check for the presence of the plugin, since it was not intended to be used outside of IG. On 7/22/2025 at 12:53 AM, Amerio.Stephane said: Could there be any way to prevent some components to be evaluated in the Editor, while some others keep be evaluated? You can modify this macro or replace it with your own: #define IG_ONLY_FOR_MASTER \ if (Unigine::Plugins::IG::Manager::get() && !Unigine::Plugins::IG::Manager::get()->isMaster()) \ return; In your own components, you can add a macro like this: #define ONLY_FOR_RUNTIME \ if (Unigine::Editor::isLoaded()) \ return; to prevent execution inside the Editor. If you want to completely disable components in the Editor or at runtime, you can add your own registration macros. #include <UnigineComponentSystem.h> #include <UnigineEditor.h> #define REGISTER_COMPONENT_EDITOR_ONLY(CLASS_NAME) \ class ComponentRegistrator_##CLASS_NAME \ { \ public: \ ComponentRegistrator_##CLASS_NAME() \ { \ Unigine::ComponentSystem::get()->addInitCallback(Unigine::MakeCallback(this, &ComponentRegistrator_##CLASS_NAME::register_component)); \ } \ void register_component() \ { \ if (Unigine::Editor::isLoaded()) \ Unigine::ComponentSystem::get()->registerComponent<CLASS_NAME>(); \ } \ ~ComponentRegistrator_##CLASS_NAME() \ { \ if (Unigine::Editor::isLoaded()) \ Unigine::ComponentSystem::get()->unregisterComponent<CLASS_NAME>(); \ } \ } __component_registrator_##CLASS_NAME; #define REGISTER_COMPONENT_RUNTIME_ONLY(CLASS_NAME) \ class ComponentRegistrator_##CLASS_NAME \ { \ public: \ ComponentRegistrator_##CLASS_NAME() \ { \ Unigine::ComponentSystem::get()->addInitCallback(Unigine::MakeCallback(this, &ComponentRegistrator_##CLASS_NAME::register_component)); \ } \ void register_component() \ { \ if (!Unigine::Editor::isLoaded()) \ Unigine::ComponentSystem::get()->registerComponent<CLASS_NAME>(); \ } \ ~ComponentRegistrator_##CLASS_NAME() \ { \ if (!Unigine::Editor::isLoaded()) \ Unigine::ComponentSystem::get()->unregisterComponent<CLASS_NAME>(); \ } \ } __component_registrator_##CLASS_NAME; and usage example: class MyСomponentWorkOnlyInEditor: public Unigine::ComponentBase { COMPONENT_DEFINE(MyСomponentWorkOnlyInEditor, Unigine::ComponentBase); COMPONENT_INIT(init) void init() { Unigine::Log::message("this message only in editor\n"); } }; class MyComponentWorkOnlyInRuntime: public Unigine::ComponentBase { COMPONENT_DEFINE(MyComponentWorkOnlyInRuntime, Unigine::ComponentBase); COMPONENT_INIT(init) void init() { Unigine::Log::message("this message only in runtime\n"); } }; class MyComponentWorkAnyway: public Unigine::ComponentBase { COMPONENT_DEFINE(MyComponentWorkAnyway, Unigine::ComponentBase); COMPONENT_INIT(init) void init() { Unigine::Log::message("this message everywere\n"); } }; REGISTER_COMPONENT(MyComponentWorkAnyway) REGISTER_COMPONENT_EDITOR_ONLY(MyСomponentWorkOnlyInEditor); REGISTER_COMPONENT_RUNTIME_ONLY(MyComponentWorkOnlyInRuntime); On 7/22/2025 at 12:53 AM, Amerio.Stephane said: Could you make some basic pre-conditions test in the problematic components to just bypass the init/update/shutdown of these? Could you clarify which components you're referring to? Are these some of the components from the IG_Aviation_addon that you're trying to run in the Editor? On 7/22/2025 at 12:53 AM, Amerio.Stephane said: We define some Console variables in the cpp Indeed, ConsoleVariable*** doesn't work properly in plugins due to initialization order issues with variables declared in the global scope. At the moment, you can use just Console::addComand: Console::addCommand("test_console", "description", MakeCallback([](int argv, char ** argc){ Log::message("console works!\n"); for (int i = 0; i < argv; i++) { Log::message("%d: %s\n", i, argc[i]); } }));
Amerio.Stephane Posted July 23, 2025 Author Posted July 23, 2025 57 minutes ago, cash-metall said: Could you clarify which components you're referring to? Are these some of the components from the IG_Aviation_addon that you're trying to run in the Editor? Yes, they are these. In our app, all components are packed in a lib, linked into all our runtime plugins. At the moment, I took the naive, more direct approach of simply also directly linking this same 'component' lib in my editor plugin. As a direct consequence, loading an object in editor which uses one of the ig aviation component immediately crashes. Hum, I think I'll try going with two libs, one for runtime-only components, and the other for editor-compatible components. Note: we went with the 'components in lib' way, because we have multiple plugins which all shall access the components. Moreover, if an editor plugin shall also have access, I think this is the only way. Would you have any opinion or advice? I like the suggestions you made for all the macros, maybe you could use them in a future sdk version directly. 1 hour ago, cash-metall said: Indeed, ConsoleVariable*** doesn't work properly in plugins due to initialization order issues with variables declared in the global scope. That's what I suspected. Is there any way I could manually 'register' these? Or could I move them from global scope to some other scope? (maybe like the REGISTER_* macro) A few more questions: I tried running the debug version of the Editor, but it always crashes. Am I missing something? Developing editor compatible components in C++ is currently very risky, as any error in handling data can crash the whole Editor. Would it be possible to consider running these in a separate process, with some kind of proxy to the real Editor process? (not really easy, I guess...)
Amerio.Stephane Posted July 23, 2025 Author Posted July 23, 2025 Hum, same thing with NET_REGISTER_COMPONENT. (just a note to not forget it later)
cash-metall Posted July 23, 2025 Posted July 23, 2025 4 hours ago, Amerio.Stephane said: we went with the 'components in lib' way, because we have multiple plugins which all shall access the components. Moreover, if an editor plugin shall also have access, I think this is the only way. Would you have any opinion or advice? In our internal projects, we use separate plugins for runtime and the editor. However, we compile the components directly from source into the plugin itself, without using an intermediate library. (I'm just sharing my experience using components in Unigine — this isn't meant to be a reference workflow. ) For example, like this: This approach allows us to use the same component files but in different way and do tricks like this (Component1.h ): #include "Manager.h" // This file will be detected in different folders depending on the build configuration. class MyComponent: public Unigine::ComponentBase { public: COMPONENT_DEFINE(MyComponent, Unigine::ComponentBase); COMPONENT_UPDATE(update); void update() { //do main logic float ifps = Manager::getIFps(); // It will be a different dt depending on the manager that was found. node->translate(Unigine::Math::vec3_forward * ifps); } #ifdef EDITOR_PLUGIN // this block will be compiled only in editor plugin COMPONENT_INIT(editor_init) COMPONENT_UPDATE(editor_validate,-1); void editor_init() { init_transform = node->getTransform(); // save original transform World::getEventPreWorldSave().connect(connections, [this]() { on_world_save(); }); // subscribe on world save callbacks World::getEventPreNodeSave().connect(connections, [this]() { on_world_save(); }); } Mat4 init_transform; EventConnections connections; void on_world_save() { node->setTransform(init_transform); // restore original transform to avoid save changes in world } void editor_validate() { // do some extra check before update editor } #endif }; In other words, we can make certain parts of the code execute only in the Editor, without affecting runtime performance. As you mentioned, "developing editor-compatible components in C++ is currently very risky", so we try to be extremely cautious — that's why we usually wrap a lot of checks in editor-only defines. 1
cash-metall Posted July 23, 2025 Posted July 23, 2025 5 hours ago, Amerio.Stephane said: I like the suggestions you made for all the macros, maybe you could use them in a future sdk version directly. I'm still not sure that using C++ components in the Editor is a good idea. We're waiting for visual scripting))) 5 hours ago, Amerio.Stephane said: That's what I suspected. Is there any way I could manually 'register' these? Or could I move them from global scope to some other scope? no, I'm afraid it won't be possible to find any workaround with it. 5 hours ago, Amerio.Stephane said: I tried running the debug version of the Editor, but it always crashes. Am I missing something? can you provide callstack or crashdump? or maybe some way to reproduce it on our side? 1 hour ago, Amerio.Stephane said: Hum, same thing with NET_REGISTER_COMPONENT. (just a note to not forget it later) yes sure. just replace REGISTER_COMPONENT inside #define NET_REGISTER_COMPONENT_RUNTIME_ONLY(CLASS_TYPE) \ REGISTER_COMPONENT_RUNTIME_ONLY(CLASS_TYPE) \ class NetworkRegistrator_##CLASS_TYPE \ { \ public: \ NetworkRegistrator_##CLASS_TYPE() \ { \ Unigine::Plugins::IG::NetworkRegistrator::get()->registrateNetworkComponent<CLASS_TYPE>(); \ } \ } __network_registrator_##CLASS_TYPE; for _Editor_only you can use REGISTER_COMPONENT_EDITOR_ONLY cause NetworkRegistator is usless in editor 1
Amerio.Stephane Posted July 24, 2025 Author Posted July 24, 2025 On 7/23/2025 at 3:24 PM, cash-metall said: yes sure. just replace REGISTER_COMPONENT inside Just for the record: this last code snippet didn't work as is. I had to used a delayed registration, like you did for the regular component: #define NET_REGISTER_COMPONENT_RUNTIME_ONLY(CLASS_TYPE) \ REGISTER_COMPONENT_RUNTIME_ONLY(CLASS_TYPE) \ class NetworkRegistrator_##CLASS_TYPE \ { \ public: \ NetworkRegistrator_##CLASS_TYPE() \ { \ Unigine::ComponentSystem::get()->addInitCallback( \ Unigine::MakeCallback(this, &NetworkRegistrator_##CLASS_TYPE::register_component)); \ } \ void register_component() \ { \ if (!Unigine::Editor::isLoaded()) \ Unigine::Plugins::IG::NetworkRegistrator::get()->registrateNetworkComponent<CLASS_TYPE>(); \ } \ } __network_registrator_##CLASS_TYPE; Otherwise, it would crash during the global variables init phase, because the Editor singleton would be created after and Editor::isLoaded() would segfault. As you said, visual scripting could help speed up dev, here :)
Recommended Posts