Experimenting With Making an Editor Plugin (Part 1)

I’m taking Mike Fricker’s advice and trying to hack together an editor plugin using the existing ability to load the game’s plugin code. This is part 1 of the quest - this is a mix of feedback and blockers.

  1. Did my code even load?
    I had that question for awhile, because of this:

There’s no [Unload] or [Recompile], this is the default view of the game module until you manually recompile the module from the top toolbar. Very confusing, because there’s no obvious state your code is in. Consider a [Compile & Load] button if you don’t want to immediately load game modules upon load of the editor. Which I can completely understand the desire not to do, in case it causes the editor to constantly crash on load.

  1. Debugging
    I’ve found this to be very difficult. If I unload the module, then attach Visual Studio to the rocket process, put a breakpoint in the modules StartupModule call, then load the module - exceptions are thrown inside the windows kernel base. Editor crashes but only if VS was attached during the Load.

One time, instead of Load and Unload, it just attached VS and clicked compile before my module had been loaded. Then when it loaded I hit the breakpoint. Awesome, only problem was every watch was garbage. It made me think everything is being compiled in Release mode. Is that correct, if so is there a way to force debug mode?

  1. Clicking compile multiple times
    This only does the work once if the code hasn’t changed. Good, but I recommend making it smart enough when manually forced by someone to compile that if nothing needs to be recompiled that it at least performs the Unload/Load. It’s a handy shortcut when trying to debug a module’s initial start-up.

  2. How can I write to the editor log?

  3. I’m starting simple, insert something into a menu.

By looking at some other editor headers, I figured out I could provide my menus/toolbars using IHasMenuExtensibility and IHasToolBarExtensibility.

Nothing appears yet, but here’s where I am in terms of constructing a menu option.

toolbarExtensions = TSharedPtr (new FExtensibilityManager());
menuExtensions = TSharedPtr (new FExtensibilityManager());

bindingContextName = FName("NicksContext");
bindingconext = TSharedPtr(new FBindingContext(bindingContextName, TEXT("Description")));

cmdName = FName("NicksCommand");
iconName = FName("IconName");

TSharedPtr cmd;
FUICommandInfo::MakeCommandInfo(
    bindingconext.ToSharedRef(),
    cmd,
    FLocCommandInfo(bindingContextName, cmdName, TEXT("CMD LBL"), TEXT("CMD DESC")),
    iconName,
    EUserInterfaceActionType::Type::Button,
    FInputGesture());

callback.BindRaw(this, &MyEditorModule::MyCallback);
menucallback.BindRaw(this, &MyEditorModule::MyMenuCallback);

commands = TSharedPtr(new FUICommandList());
commands->MapAction(cmd, menucallback);

menuHookName = FName("HelpBrowse", FNAME_Find);
TSharedPtr extender = TSharedPtr(new FExtender());
extender->AddMenuExtension(menuHookName, EExtensionHook::Position::First, commands, callback);

menuExtensions->AddExtender(extender);

I feel like I’m close, but I suspect the I’m missing some information when it comes to the BindingContext. Like without the right binding context the menu won’t appear.

  1. I figured out how to find the editor hooks. Preferences > Developer Tools > Display Multibox Hooks. Very useful.

  2. I have an awesome idea for an editor plugin but I need to be able to add to the context menu of the blueprint editor, but unlike everything else I’ve encountered it appears to be the one thing that has no multibox hooks. Is this a rendering bug or just an overlooked place to add hooks.

  1. Can the beta forum be modified to contain more image uploads per post? It’s capped to 2.
  1. Found GLog, GLogConsole, GWarn, GError

Hey, about the multibox extensions. This was a relatively new feature added, so the entire editor has yet to have all the proper hooks added. As you found, most of the context menus don’t have hooks yet (only the Level Editor context menu so far), but this is being worked on as we speak!

About using them, a user will want to be able to insert a multibox hook into the Static Mesh Editor’s File menu, but not in any other File menu, for example. Thus, you need to specify which editor you will be extending. If you look in StaticMeshEditorModule.h, you’ll see that the module inherits IHasMenuExtensibility and IHasToolBarExtensibility. These allow you to get the FExtensibilityManagers of this editor, which is where you should add your FExtenders.

Technically, you should only ever create your own FExtensibilityManager if you want other users to extend your own plugin.

Hey Nick! We really appreciate this feedback. Sorry it took me a few days to get back to you.

  1. Thanks, this is a bug where game modules with UObjects would not be correctly discovered by the module UI due to a bug with package name handling. This prevented the Recompile button from showing up for those modules. I checked in a fix this today, so the next build will have it. I also made a change that fixed game-specific modules that did not have UObjects from being unloadable/reloadable at all.

  2. Yes, the entire engine is compiled with optimizations turned on. We do generate debug symbols for the engine code, but those symbols are not distributed outside of Epic currently. This is something that may change in future builds. All of your game or plugin code should be compiled with full debug information, and you should be seeing those symbols in the debugger. Unfortunately in this case it probably crashed in an engine call stack, so without symbols it’s going to be hard for you to tell what’s going on. Definitely interested in hearing more feedback about this.

  3. Thanks for the suggestion. We’re looking into multiple issues with the Recompile button now.

  4. You can write to the log by doing:

DEFINE_LOG_CATEGORY( LogMyCategory );

UE_LOG( LogMyCategory, Log, TEXT( “This will be written to the log, as well as the debugger console.” ) );

  1. One key thing you’re missing here is that the FUICommandInfo object that you create must persist for the lifespan of that command. Basically the UI command info is the persistent representation of your UI feature, something that is known at editor startup time and the user can discover and bind to a key using the keybinding editor. You should create your UI commands at your module’s startup, then map the actions later on, when your widgets are constructed. I apologies for the lack of documentation in this area, but we’ll be working on it over time. The binding context is just a unique “scope” for your commands, so that we can organize them properly in the keybinding editor, and route input events properly when multiple possible handlers are available.

  2. Awesome, glad you found that feature. Maybe we should rename that from ‘Multibox’ to something more natural though, like ‘display extension points’?

  3. Wow, that’s excellent how much progress you made. You’re the first person to try to use those APIs outside of Epic as far as I know. As you can see, most tool bars and top level menu bars are already customizable, for the most part. However, many dynamically spawned menus, such as right click context menus aren’t yet customizable. We’re working on this right now though. I’ll try to make sure that at least the most common sub-editors have customizable contextual menus by the next release. Please continue to send feedback if you find other areas that aren’t extensible enough!

  4. I’ll follow up with our support teams about this. Thanks for the suggestion!

–Mike

  1. Sounds good. I can hope the debug symbols will be distributed outside the licensee community, but I can understand why they wouldn’t be. If you feel the size is too cumbersome to be included in the installer, consider setting up a symbol server for them to let VS pull them directly or a separate installer.

  2. Yeah, extension points would be a better name. Just make sure the API variable names and doc reflect the new name.

Also, with optimizations turned on, it complicates a lot of things, even though I have debugging symbols, the stack isn’t always trustworthy in release and so you get into a lot of situations where the debugger claims variable x isn’t defined, and so you can’t sniff the data on it.

I would highly recommend adding a toggle to compile user game/editor modules in debug mode.

I also found the reasoning for the crash I was seeing when I reload the module:

Objects have the same fully qualified
name but different paths. New Object:
MyEditorCharacter
/Script/MyEditor.Default__MyEditorCharacter
Existing Object: MyEditorCharacter
/Script/MyEditor.Default__MyEditorCharacter

Though the paths appear the same, might be the bug you mentioned. I just realized you guys also write the message with OutputDebugString. Totally missed that. Consider doing an assert dialog instead with the same text if a debugger is attached?

Hey Mike, thanks for all your help/attention so far - it’s been greatly appreciated.

I’ve been sitting here trying to create a menu option for like the last 3 hours and I think I may know why nothing is working. First, I found the correct way to provide an extender.

FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor"));
LevelEditor.GetMenuExtensibilityManager()->AddExtender(myextender);

But still - nothing appears and no extender delegates are called and I think I get why. I’ve started to suspect that menus are only constructed once - or to be more precise, the various extenders are only combined once - then that combined extender is used to seed the menu’s construction: see - FMainFrameModule::CreateMainMenu

And if your extender is not part of the combined extender when it’s created you’ll never become part of the menu, or get any callbacks for it. And since the game module is always loaded last, it would explain why I would always miss the opportunity to join the menu.

Assuming I’m correct, should extension managers have an event that fires when they change to notify their owners to recombine and recreate the menu widget?

Is there an exposed way to manually force a recreation of the main menu if you’ve added a new extender to an extension manager.

I raised the max characters for comments (10000) and the number of attachments (10) allowed as well as max size (10MB total for all attachments). Hopefully that makes things more pleasant for posting. A lot of the settings were still set to the defaults, which are probably not ideal.

Awesome, thanks Jeff :slight_smile: