Editor Window/Module for Modifying a Non-File-Based Actor Instance

Okay, the title is odd. The gist is this:

  • I want to create an editor extension that will bring up a window with an instance of an actor unique to that window (for its entire lifetime).
  • This actor instance will never be saved nor loaded from any other package or on-disk file; it’s purely just a way to display the actor while the user customizes its data.
  • The data itself is stored as a JSON file outside of the standard /Content/ dir (because… reasons).
  • So, basically, I want to be able to customize an instance of this actor in a viewport while modifying the JSON config values, hit my button to save/load differing JSON files, then close the window, and go on my merry way.

I’ve probably spent a few weeks on figuring out how to properly handle this whole thing (and, by now, I wish I would have just written an external application to do it, but then I’d lose the viewport). For one thing: this is essentially a transient actor instance; it’s not to be saved nor loaded to disk. Beyond that, nothing that actually occurs within this editor has any practical effect on any other part of the game. It’s purely a visualization/authoring tool for my team (well, me) to customize base JSON files that get serialized/deserialized as-needed – this last part is all working and is copacetic.

I first tried just doing this by just creating a blueprint dummy file based on my primary C++ class for this actor, and then just extract data as properties changed in that editor. This… Did not work. The editor was very attached to any values set in a textbox or asset string reference dropbox (I could save the values out, but I could never load anything in, even when trying to manually set the properties – which were transient anyway).

So, then I looked at the StaticMeshEditor – since it’s got a viewport and a side-panel which is all I need in the world (oh to be as young and naive and hopeful as I was two weeks ago), but then realized that I was running into issues because that editor doesn’t operate on instanced data, it operates on an asset that exists on the local filesystem. So, hacking my way around that proved useless.

So, I re-wrote everything again, this time just started building off an FAssetToolkit-based window that launches a single tabbed container with one tab. Got the detail view all working (ish), and am now here:

That it appears and the detail view is customized more-or-less right (ignore the viewport, I could care less about that until everything else is resolved). So that’s neat. But: if this editor is opened at any time in the editor, it will always result in a exit-time editor (like UE4Editor, not this custom editor) crash. And I’m relatively certain at this point that it’s just due to some reference or pointer or distance relative somewhere not being properly cleaned up but… I AM SOOOO WORN OUT FROM THIS. Digging through the engine guts to figure out how to start this initially was rough, but digging through even more and more to figure out how to “actually” do things properly only to have crashes way, way deeper in the engine’s shutdown routine. It wounds me.

(Working on tools is my least favorite thing in the universe).

I’m sure that this is a somewhat unusual approach to an editor in UE, but the purpose of it is immutable, I just need to figure out how to get that purpose working. This isn’t really an AnswerHub thing since I’m sure it’s on my end somehow, but the dearth of information/documentation is making this exceedingly annoying. So here’s my last call-stack (and as I try and clean up more, the exact crash changes, but it’s always on shutdown).


LoginId:f0f3b82d4ae2ef5c62a9918e8cbc3f5d
EpicAccountId:017ebfc9f29f4134821504ead7d1034f

Access violation - code c0000005 (first/second chance not available)

UE4Editor_MechEditor!SharedPointerInternals::TReferenceControllerWithDeleter<AMech,SharedPointerInternals::DefaultDeleter<AMech> >::DestroyObject() [f:\joymachine\joyengine\engine\source\runtime\core\public	emplates\sharedpointerinternals.h:110]
UE4Editor_MechEditor!FMechEditorModule::~FMechEditorModule()
UE4Editor_MechEditor!FMechEditorModule::`scalar deleting destructor'()
UE4Editor_Core!FModuleManager::UnloadModule() [f:\joymachine\joyengine\engine\source\runtime\core\private\modules\modulemanager.cpp:571]
UE4Editor_Core!FModuleManager::UnloadModulesAtShutdown() [f:\joymachine\joyengine\engine\source\runtime\core\private\modules\modulemanager.cpp:692]
UE4Editor!FEngineLoop::Exit() [f:\joymachine\joyengine\engine\source\runtime\launch\private\launchengineloop.cpp:2776]
UE4Editor!GuardedMain() [f:\joymachine\joyengine\engine\source\runtime\launch\private\launch.cpp:177]
UE4Editor!GuardedMainWrapper() [f:\joymachine\joyengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:134]
UE4Editor!WinMain() [f:\joymachine\joyengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:210]
UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253]
kernel32
ntdll

So, I get the callstack. There’s a shared pointer (the actor instance for the editor) that’s likely not being cleaned up before the object it is referring to is destroyed by, likely, the editor/world. I just can’t seem to figure out a good solution to get around it.

hugs,
trent

Also, for what it’s worth, I’m going to make it a bit more abstract (ie, remove my project-specific stuff) and post all of the code for what ends up doing the job to our repo so that there’s at least a decent, game/user-focused example that’s fully fleshed-out so, hopefully, no one that wants to do something similarly has to deal with this ever again.

A couple more for good measure (which, really, doesn’t help, but tossing them in here in case it does). It’s almost definitely some kind of reference being held to a destroyed object – which means it’s likely unable to be easily reproducible ever, but I’m running pretty low on ideas. I’ve stripped everything of all but the most bare-bones of functionality to launch and shut down a tabbed window.

(Both are from debug builds, and when I tried to stop at a decent point pre-crash, things looked more-or-less in order, and at the time of the crash, nothing stood out other than outstanding references in objects I don’t have any control over).



[2017.07.23-00.22.08:067][654]LogWindows:Error: Assertion failed: SharedThis.Get() == this [File:F:\joymachine\joyengine\Engine\Source\Runtime\Core\Public\Templates/SharedPointer.h] [Line: 1215] 
[2017.07.23-00.22.08:087][654]LogWindows:Error: 
[2017.07.23-00.22.08:087][654]LogWindows:Error: 
[2017.07.23-00.22.08:087][654]LogWindows:Error: 
[2017.07.23-00.22.08:087][654]LogWindows:Error: KERNELBASE.dll!RaiseException()
[2017.07.23-00.22.08:108][654]LogWindows:Error: UE4Editor-Core.dll!FOutputDeviceWindowsError::Serialize() [f:\joymachine\joyengine\engine\source\runtime\core\private\windows\windowsplatformoutputdevices.cpp:120]
[2017.07.23-00.22.08:128][654]LogWindows:Error: UE4Editor-Core.dll!FOutputDevice::Logf__VA() [f:\joymachine\joyengine\engine\source\runtime\core\private\misc\outputdevice.cpp:70]
[2017.07.23-00.22.08:151][654]LogWindows:Error: UE4Editor-Core.dll!FDebug::AssertFailed() [f:\joymachine\joyengine\engine\source\runtime\core\private\misc\assertionmacros.cpp:349]
[2017.07.23-00.22.08:173][654]LogWindows:Error: UE4Editor-UnrealEd.dll!TSharedFromThis<FAssetEditorToolkit,0>::AsShared() [f:\joymachine\joyengine\engine\source\runtime\core\public	emplates\sharedpointer.h:1219]
[2017.07.23-00.22.08:196][654]LogWindows:Error: UE4Editor-UnrealEd.dll!FAssetEditorToolkit::CloseWindow() [f:\joymachine\joyengine\engine\source\editor\unrealed\private	oolkits\asseteditortoolkit.cpp:434]
[2017.07.23-00.22.08:220][654]LogWindows:Error: UE4Editor-UnrealEd.dll!FAssetEditorManager::CloseAllEditorsForAsset() [f:\joymachine\joyengine\engine\source\editor\unrealed\private	oolkits\asseteditormanager.cpp:166]
[2017.07.23-00.22.08:244][654]LogWindows:Error: UE4Editor-MechEditor.dll!FMechEditor::OnButtonClose() [f:\joymachine\steelhunters\source\mecheditor\private\mecheditor.cpp:126]
[2017.07.23-00.22.08:266][654]LogWindows:Error: UE4Editor-MechEditor.dll!FMechEditor::~FMechEditor() [f:\joymachine\steelhunters\source\mecheditor\private\mecheditor.cpp:90]
[2017.07.23-00.22.08:287][654]LogWindows:Error: UE4Editor-MechEditor.dll!FMechEditor::`scalar deleting destructor'()
[2017.07.23-00.22.08:312][654]LogWindows:Error: UE4Editor-UnrealEd.dll!SStandaloneAssetEditorToolkitHost::~SStandaloneAssetEditorToolkitHost() [f:\joymachine\joyengine\engine\source\editor\unrealed\private	oolkits\sstandaloneasseteditortoolkithost.cpp:211]
[2017.07.23-00.22.08:334][654]LogWindows:Error: UE4Editor-Slate.dll!SDockTab::~SDockTab()
[2017.07.23-00.22.08:355][654]LogWindows:Error: UE4Editor-Slate.dll!SDockingArea::OnOwningWindowBeingDestroyed() [f:\joymachine\joyengine\engine\source\runtime\slate\private\framework\docking\sdockingarea.cpp:429]
[2017.07.23-00.22.08:376][654]LogWindows:Error: UE4Editor-Slate.dll!TBaseSPMethodDelegateInstance&lt;0,SDockingArea,0,TTypeWrapper&lt;void&gt; __cdecl(TSharedRef&lt;SWindow,0&gt; const & __ptr64)&gt;::Execute() [f:\joymachine\joyengine\engine\source\runtime\core\public\delegates\delegateinstancesimpl.h:327]
[2017.07.23-00.22.08:396][654]LogWindows:Error: UE4Editor-SlateCore.dll!SWindow::RequestDestroyWindow() [f:\joymachine\joyengine\engine\source\runtime\slatecore\private\widgets\swindow.cpp:1186]
[2017.07.23-00.22.08:430][654]LogWindows:Error: UE4Editor-Slate.dll!SWindowTitleBar::CloseButton_OnClicked() [f:\joymachine\joyengine\engine\source\runtime\slate\public\framework\application\swindowtitlebar.h:427]
[2017.07.23-00.22.08:451][654]LogWindows:Error: UE4Editor-Slate.dll!TMemberFunctionCaller&lt;SWindowTitleBar,FReply (__cdecl SWindowTitleBar::*)(void) __ptr64&gt;::operator()&lt;&gt;() [f:\joymachine\joyengine\engine\source\runtime\core\public\delegates\delegateinstanceinterface.h:165]
[2017.07.23-00.22.08:472][654]LogWindows:Error: UE4Editor-Slate.dll!UE4Tuple_Private::TTupleImpl&lt;TIntegerSequence&lt;unsigned int&gt; &gt;::ApplyAfter&lt;TMemberFunctionCaller&lt;SWindowTitleBar,FReply (__cdecl SWindowTitleBar::*)(void) __ptr64&gt; &gt;() [f:\joymachine\joyengine\engine\source\runtime\core\public	emplates	uple.h:497]
[2017.07.23-00.22.08:494][654]LogWindows:Error: UE4Editor-Slate.dll!TBaseSPMethodDelegateInstance&lt;0,SWindowTitleBar,0,FReply __cdecl(void)&gt;::Execute() [f:\joymachine\joyengine\engine\source\runtime\core\public\delegates\delegateinstancesimpl.h:327]
[2017.07.23-00.22.08:516][654]LogWindows:Error: UE4Editor-Slate.dll!TBaseDelegate&lt;FReply&gt;::Execute() [f:\joymachine\joyengine\engine\source\runtime\core\public\delegates\delegatesignatureimpl.inl:537]
[2017.07.23-00.22.08:538][654]LogWindows:Error: UE4Editor-Slate.dll!SButton::OnMouseButtonUp() 




[2017.07.23-00.28.05:377] 97]LogWindows:Error: Assertion failed: Index >= 0 [File:F:\joymachine\joyengine\Engine\Source\Runtime\CoreUObject\Public\UObject/UObjectArray.h] [Line: 455] 
[2017.07.23-00.28.05:377] 97]LogWindows:Error: 
[2017.07.23-00.28.05:377] 97]LogWindows:Error: 
[2017.07.23-00.28.05:377] 97]LogWindows:Error: 
[2017.07.23-00.28.05:377] 97]LogWindows:Error: KERNELBASE.dll!RaiseException()
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-Core.dll!FOutputDeviceWindowsError::Serialize() [f:\joymachine\joyengine\engine\source\runtime\core\private\windows\windowsplatformoutputdevices.cpp:120]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-Core.dll!FOutputDevice::Logf__VA() [f:\joymachine\joyengine\engine\source\runtime\core\private\misc\outputdevice.cpp:70]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-Core.dll!FDebug::AssertFailed() [f:\joymachine\joyengine\engine\source\runtime\core\private\misc\assertionmacros.cpp:349]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-MechEditor.dll!FMechEditorModule::ShutdownModule() [f:\joymachine\steelhunters\source\mecheditor\private\mecheditormodule.cpp:95]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-Core.dll!FModuleManager::UnloadModule() [f:\joymachine\joyengine\engine\source\runtime\core\private\modules\modulemanager.cpp:565]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor-Core.dll!FModuleManager::UnloadModulesAtShutdown() [f:\joymachine\joyengine\engine\source\runtime\core\private\modules\modulemanager.cpp:692]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor.exe!FEngineLoop::Exit() [f:\joymachine\joyengine\engine\source\runtime\launch\private\launchengineloop.cpp:2776]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor.exe!GuardedMain() [f:\joymachine\joyengine\engine\source\runtime\launch\private\launch.cpp:177]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor.exe!GuardedMainWrapper() [f:\joymachine\joyengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:134]
[2017.07.23-00.28.05:377] 97]LogWindows:Error: UE4Editor.exe!WinMain() [f:\joymachine\joyengine\engine\source\runtime\launch\private\windows\launchwindows.cpp:210]


Hey Trent. I’ve seen bits and pieces of the game you’re working on (On UnrealSlackers maybe? Not sure). Anyway, looks very cool.

Yeah, understanding the UE4 editor codebase is a painful process. At a guess from what I can see, you’re using a TSharedPtr< AActor >? If so, bad idea. UObjects have their own garbage collection so should never be used with shared pointers. You just need to create your actor using SpawnActor on the preview world that your viewport is attached to, and ideally then store it as a TWeakObjectPtr< AActor >. That’s a special smart pointer type for UObjects, doesn’t interfere with destruction, just monitors the object and nulls out the pointer if the actor is destroyed.

I’ve got a fair bit of experience with this kind of editor code; give me a shout if you need a pointer on anything, fair chance I’ve already faced it.

Thanks much, sir.

I think I tried it as a TWeakPtr at some point? I had no idea that there was such a thing as TWeakObjectPtr (it wasn’t always a shared pointer either; it’s gone through a whoooole lot of permutations). I’ve mostly abandoned this thing as it ended up being way more time than I think it warrants (I’m just wrapping up a standalone app that’s little more than a series of text entry boxes as well as some drop-downs that scan relevant directories for UE4 assets, which I then just transform into a FStringAssetReference-style path. It’s not perfect, but it takes about two days total and I can just run it alongside UE4, save out new data, deserialize in UE4, and go from there.

That said, this is still something I’d like to conquer with a giant rocket to the face, so I’ll give it a try. :slight_smile:

hugs

Yup, UE4 editor extension is on the whole pretty flexible and powerful. Problem is, there’s a ridiculous amount of boilerplate code needed to get even simple things up and running. That’s bad enough when you’ve already worked out how to do it, let alone when you’re trying for the first time…

Yeah, it is very flexible, but it is also non-trivial to learn (and, I’d imagine, still not quick to do once you do learn). I hate tools work and this was the last thing I wanted to spend the last few weeks on, so that didn’t help.

The biggest issue was — at least, I think — that I wasn’t making something that was a pure asset editor. It was a modal window (not in those terms, as I quickly discovered modal windows are awful in Slate) that had to have a dummy object created to serve as it’s “edited object” for its lifetime. I’ve been able to make asset types/editors fairly easily in the past, but not something quite like this.

Yeah, I’ve made simple tool windows without much issue, but it wouldn’t surprise me if adding things like a viewport and such turned out to be a big hassle if you’re not deriving from an asset editor. Can totally understand going the external app route.

Well, ****.

Yup, just switching to TWeakObjectPtr did the trick for fixing the crash.

<3