What I Wish I'd Known About Writing Extensions for Unreal Editor

7 min read

In a previous blog, I announced a plugin for the Unreal Editor that allows for importing and exporting table data to and from Dolt, our version-controlled SQL database with Git-like semantics. You can find that plugin here.

I've already talked about the plugin itself, but I wanted to go a little bit more detail on the development process. Because while there's a lot of good tutorials on making games in Unreal, resources about extending the editor are more scarce. And there's a lot that I wish I had known going in.

Prior to this experiment, I'd never used Unreal Engine before, although I'd heard secondhand about how flexible it was. And overall, the experience left me impressed. Once I got my feet wet, I was able to make extra configuration menus, context menus, and console commands with relative ease, all existing seamlessly within the editor.

Unfortunately, I was less impressed with the build tooling. I suspect that I spent more time wrestling with compiling the engine, building my plugin, and configuring the debugger than I spent actually writing code.

I get the impression that Epic focuses their polish into the parts of the engine that users are most likely to encounter. I don't blame them for this: Unreal Engine is over 25 years old, and in that time it's grown into a truly massive piece of software. Any project at this scope requires a prioritization of resources.

But this does mean that going off the beaten path comes with its share of rough edges. And for most of the issues I encountered, I could find neither official documentation about them, or satisfying writeups from other users. My hope is that by documenting them here, this might be useful for whoever comes along next.

The Pain Points

These were my biggest gripes:

Many features require rebuilding the engine from source

While it's possible to download Unreal Engine directly from the Epic Games Store launcher, this build is missing some features, presumably to shrink the download size. Version 5.3 STILL clocks in at 44 GB for the core components alone, and the debug symbols add an additional 90 GB, so some amount of shrinkage was probably necessary.

But one of the features that gets stripped out of the prebuilt is building test targets. If you want to run Unreal's unit and automation test features, you need to build the engine from source. Unfortunately, building from source wasn't simple, because...

Mac support is limited

In fact, multiple devs that helped me with this were surprised that there even was a Mac build of the Unreal Editor, and one of them insisted straight-faced that there wasn't, even thought I'd already been using it for a week.

It's hard to find an exact timeline on how long Unreal Editor has been available for MacOS. Notably, while it's possible to download previous Mac versions of the editor all the way back 4.0 (March 2014), the engine documentation didn't include a section on MacOS development requirements until 5.2 (May 2023). The consensus seems to be that Mac support has always been lackluster anyway. That may be improving soon, but for now it seems like anyone trying to develop on a Mac is going to have extra problems.

While the prebuilt binary can ship with debug symbols, I wasn't able to actually load those debug symbols in VSCode. This is a known issue, but the suggested workarounds didn't help: the only solution that actually worked for me was rebuilding from source and generating the debug symbols myself.

While game development on Macs can use either XCode or VSCode, if you want to build the engine from source then only XCode is officially supported. And even then, some of the tagged releases had compile errors when XCode attempted to build them. I'm sure that I was doing something wrong with my configuration, because I highly doubt that Epic would actually cut a release that didn't build on one of their officially supported platform. But finding out what I was doing wrong was an exercise in frustration because...

Documentation is inconsistent and sometimes missing

Oftentimes the documentation is vague enough that it's useless for troubleshooting problems. ISourceControlProvider is an interface for interacting with source control, but correct usage essentially requires reading the editor's source code to understand exactly how the interface is actually meant to be used. Otherwise there's no indication that, to pick an example, the GetState method depends on a previously run UpdateStatus command in order to get up-to-date info from the server.

On several occasions, the official documentation at https://docs.unrealengine.com contained errors. Sometimes it was little things, like a method that had a different return type than the documentation claimed. Another time, one of the classes I was including was undocumented in version 5.3, but was documented in earlier versions. At first I thought that this class had been removed or deprecated. It hadn't. It was still in the code, the documentation was just accidentally dropped.

At times I needed to run an Unreal executable or batch script from the command line. But the set of flags for these commands are poorly documented. Again, this seems to be a case of optimizing for the most common workflow, which is to have the editor invoke these commands instead of using them yourself.

The documentation page for engine command line flags was pared down in 5.2, removing many of the flags that were listed in 5.1. These flags weren't removed as far as I can tell, just no longer documented. And command line flags for launching the editor aren't documented at all. I used the -nullrhi flag in my test harness in order to launch the editor in headless mode... but that flag appears nowhere in the documentation, and I only learned about it from someone in the forums.

Still, I was eventually able to build the engine from source, so no harm no foul, right? Well no, because...

Test Targets have hidden requirements

Even after building the engine from source, I wasn't able to build any test targets. One of the core modules that I depended on, Slate, would fail to compile. This only happened on test modules. Building my code as an editor module did not have this problem.

As far as I could tell, test targets have additional module dependencies: my test target depended on the DeveloperSettings module, even though my Game target which contained the exact same source classes didn't. And in order for the build system to locate necessary header files, I needed to make that dependency explicit by including DeveloperSettings in my buildfile, even though:

  • My code did not directly depend on it.
  • This dependency could have been inferred by the fact that it was a test target and (as far as I know), all test targets have this dependency.

Even after adding this to my buildfile, I continued to get include errors for other modules. And adding those modules to my buildfile didn't fix the issue. I wasn't able to find anyone online with a similar issue either.

I never figured out how to actually build my test target. But it turns out even if I had, it wouldn't have mattered because...

Test Targets can't test the Editor

The Unreal Editor is itself written in Unreal. This is actually really cool because it means that your game can run within the editor, and the editor natively interfaces with the game's runtime. This also means that some parts of the codebase are only compiled for the editor and don't exist in the final game. This is enforced by a combination of C preprocessor directives, and a check in the build tool that verifies non-Editor targets don't depend on Editor code. Code affected by this includes all the modules that make up the editor itself, but it also includes functionality within game classes, such as the code for importing and exporting data tables.

Since a build target can't be both a Test target and an Editor target, this means that Test targets can't depend on any of this code either. And since the whole point of my plugin was to extend the editor to automate importing and exporting of data tables, this turned out to be a huge problem.

So how did I actually overcome these issues? What things do I wish I knew when I started? Here's a couple of takeaways:

The Lessons

Don't Develop on MacOS

There's no beating around this bush. The documentation may present all three host OSes as equal choices, but they clearly aren't, at least as of writing. Prefer Windows or Linux as your primary development environment and you'll have a more polished experience.

You can test Editor features by running the Editor in Headless Mode

Automated testing is important, especially when there's a large number of configurations where the code under test might be used. But that doesn't mean you have to be married to any particular testing framework.

If Unreal's built in testing options don't suit your needs, you can build your own by running your game or the editor in headless mode. Passing the -nullrhi flag on the command line will cause Unreal to skip creating a window for the process. (Note that there's only a single dash, not double.) Since there's no UI, you need to also use the -ExecCmds flag, which causes the engine to run a list of console commands on startup. For instance, I defined a custom console command called RunAllDoltTests and passed -ExecCmds="RunAllDoltTests, Quit" to run the tests and exit afterward.

Afterthoughts

Looking back on this experiment, I'm of two minds about Unreal Engine. Because on one hand, the runtime libraries are powerful and a charm to work with. But on the other hand, the build framework was an absolute nightmare. And when things went wrong, the documentation was completely unhelpful.

I know that my specific problems won't affect most developers. Most devs aren't extending the editor. Most devs aren't interacting directly with source control, or spawning subprocesses, both of which had their own undocumented pitfalls. But if my experience is any indication, a dev doing anything off the beaten path will surely stumble on their own roadblocks.

All in all, whether or not Unreal has a steep learning curve ultimately depends on what specifically you're trying to do. Someone with more experience with the engine is going to be in a stronger position to navigate these surprises and work around them.

As always, if this made you feel anything, join our Discord and let us know.

SHARE

JOIN THE DATA EVOLUTION

Get started with Dolt

Or join our mailing list to get product updates.