Skip to content

DarkEdif ext dev features

Phi edited this page Jul 12, 2022 · 15 revisions

DarkEdif SDK is a continuation of Edif SDK with changes mostly coded by Phi, with some code contributions from LB and other developers. CT forum thread.

This page is a breakdown of extension developer's features, including those added silently, those activated by tools or keywords, and more "hidden" options.

Passive features

It includes all the features of Edif and some extra, including:

  • Multi-language JSON file
    See Bluewing Server's JSON for example; search for "French".
  • Properties defined in the JSON file
    See DarkEdif Template for properties available.
  • Smart properties - you can edit your object properties between extension versions. As long as you increment Extension::Version, it will "just work".
  • Error messages during Edif crash scenarios (e.g. missing actions)
    Not every Edif crash is prevented, but quite a few are checked for at runtime.
  • Minified SDK
    All unnecessary editor functions have been removed from compilation; in most cases Runtime.cpp is not necessary at all.
    The DarkEdif Template still has examples for all functions removed if you need them; uncomment and they'll work.
    On related note, the Ext.def file has been removed, as now the #pragma DllExportHint to make the function visible to Fusion. The runtime has also been minified.

Check that JSON and C++ parameters match

This is done by C++ template metaprogramming, with message boxes shown when object is created in Run Application and built EXEs. It is only active in Debug and Debug Unicode builds.

It can be disabled by defining FAST_ACE_LINK in project properties, but this is strongly discouraged, as Fusion limits the parameter types that can be used to very specific types, and trying to "trick" the SDK to letting you use the wrong types is will cause crashes from the processor reading wrong CPU registers.

JSON minifier

As only some parts of the JSON are read by the SDK at runtime, the SDK features a JSON minifier, which is automatic.
Whenever you build for Runtime or Runtime Unicode, a tool runs before compilation starts. In the tool:

  1. DarkExt.json, Extension.cpp, and Extension.h have their modified dates read.
  2. DarkExt.PostMinify.json is looked for.
    If not found, the PostMinify JSON will be generated.
    If found, the date stored in the first line is compared against the most recent out of the three files in step 1. If the date is lesser - i.e. a change has been made to one of those - then the PostMinify JSON is regenerated again.
  3. DarkExt.json is read, and all parts not used by Runtime will be stripped and put in DarkExt.PostMinify.json.

The JSON minifier works like this:

  • Comments are removed
  • All whitespace outside of text will be removed
  • Actions, Conditions, Expressions titles and parameters names removed; the parameter/return types are kept, as well as the Triggered property for conditions
  • Properties' names are used by EDITDATA reading functions (at least in this version of DarkEdif), so they are kept.
  • Any other language bar the first one is stripped entirely

The PostMinify JSON is handled separately in json.cpp (see the end of the file) and is expected to be in minified format during Runtime compilation by the resource compiler.

Smart object properties

Smart object properties are automatically active since SDK version 14, released July 2022.
Smart properties store the type, name and data for properties in EDITDATA, and they convert by checking Extension::Version against the version number stored in the MFA file. When it does need converting, the system copies or converts to the new version as needed.
Any object properties under the smart property system can be:

  • renamed – specify an OldTitle in the JSON to have old version's data copied to new version
  • reordered – data will be copied to new version with no extra fanfare
  • deleted – their old version's data will be discarded
  • added to – properties that only exist in the new version will be given their default values from the JSON's DefaultState.
  • have their type changed – there is some support for converting types to similar property types; for example, a numeric editbox to a numeric up-down control.

It is required that you increment Extension::Version between changes in properties, or any changes in your own data you might store in the EDITDATA struct.
During development of a new version, you should avoid saving any example MFAs you're using with the incremented Version lest you confuse the smart property system. For example, if you're creating a new version 3 to replace version 2, only save the MFA once you've finalized version 3's properties, as the MFA's version 3 property layout will be stored.
If you're not changing the properties or EDITDATA at all for your new update, then you needn't increment Extension::Version, but be aware Extension::Version is also used by the DarkEdif update checker.

There is also an converter to upgrade from the pre-smart property DarkEdif layout to the smart layout. If you choose to do this, increment Extension::Version, and don't alter the JSON properties. This upgrade will fail if the properties are changed between the pre-smart and smart versions, as it expects the current JSON to match the pre-smart layout.

It is safe to update from version 2 with smart properties, to version 6 with smart properties, and so on. As long as the old version saved in the MFA has smart properties, there is no chance of data loss.

In the extension's code, you can read the smart properties by these two functions, but only inside the Extension constructor:

  1. edPtr->Props.GetPropertyStr("property title"sv)
    Newlines are CRLF on Windows and LF elsewhere.
  2. edPtr->Props.IsPropChecked("property title"sv)

These functions will work on all platforms. In the Extension constructor, copy any values you need into the Extension class.

You can also call these functions with property index, which is 0+ and includes any uneditable properties. Since properties can be moved, it's recommended you call the variants with property names instead.

Disabling smart properties

You can disable smart properties, and write/read properties from EDITDATA manually, by defining NOPROPS in project properties.
This will still enable JSON properties, i.e. it will still generate the PropData[] used to populate Fusion's object property pane, but the handling of properties' data and checkboxes - as a bare minimum, GetPropValue(), GetPropCheck(), SetPropValue(), SetPropCheck() must be coded manually for each property by the extension developer, like in Edif and the older SDKs.

Active features

The following is a list of all features that must be activated by the extension developer.

Extension description defined at runtime

Only active during Editor builds.
If JSON_COMMENT_MACRO is defined, the extension description (or "comment") is put in a sprintf_s(), with the parameter list set to the content of JSON_COMMENT_MACRO.
JSON_COMMENT_MACRO is usually defined in Common.h. As the parameters will be evaluated at runtime, it's worth double-checking them against the sprintf parameter list.

An example can be seen in Bluewing Client (Common.h, DarkExt.json) and Bluewing Server (Common.h, DarkExt.json).

Fusion debugger access

Accessed via DarkEdif::FusionDebugger, and meant to be used with lambda functions, otherwise known as anonymous functions.
The feature is activated by definition USE_DARKEDIF_FUSION_DEBUGGER, which is set automatically by the DarkEdifPreBuildTool, when FusionDebugger is detected inside Extension.cpp.

See examples Bluewing Client (Extension.cpp, Extension.h), and Bluewing Server (Extension.cpp, Extension.h).

DarkEdif uses a pre-build standalone tool (and in non-Windows platforms, also a post-build standalone tool) programmed in C#. This tool is currently not open-source.

SDK update checker

The SDK update checker is activated by adding RunUpdateNotifs() in Edittime.cpp, which is normally done inside EditorDisplay(). When this function is added, the PROPS file will automatically define USE_DARKEDIF_UPDATE_CHECKER=1.

The update checker can be disabled or modified by the Fusion user's DarkEdif.ini file; your DarkEdif.ini also affects the update checker, like it does every other DarkEdif feature.

It's worth noting that this check is sent under basic HTTP 1.1, not HTTPS, as adding an encryption library to every DarkEdif extension would bloat extensions unnecessarily.

Details sent:

  • Project name; e.g. "DarkEdif Template"
  • Project configuration; e.g. "Debug" or "Edittime Unicode"
  • Extension::Version variable; e.g. "12"
  • DarkEdif::SDKVersion; e.g. "1"

The URL queried will look up an internal database, and either report no update, a minor update, a major update, or an error. Updates are set to major or minor by the extension developer, and a major one normally indicates a crash or otherwise important fix/improvement.

All serious errors are reported to the user. More errors are reported to testers and extension developers, who are detected by the page via the version they're using and the project configuration, respectively.
If built in Debug configuration, the SDK version is compared against the latest and an DarkEdif SDK version check is done too, to report if the DarkEdif SDK is out of date and the extension will be missing SDK fixes.

TODO: Expand

INI for code FusionSDKConfig.ini

In the repository folder, you can add a FusionSDKConfig.ini file. Several items in FusionSDK.props look for this INI file and read settings from it during the project load or build process. The file should be UTF-8 with Unix (LF) line endings.
Spaces around the "=" are permitted, and you can use "; comment" comments.

PDB during release builds

PDBs come in two flavours:

  • Stripped debug symbols with just function names/addresses.
  • Full debug symbols, with the entire code file.

Debug/DU builds always have full PDB files, as the less information the less accurate a debugging of that code. Without even stripped symbols, a crash cannot be stepped backwards from the crash function to the function that called it, and so on, which makes debugging harder.
It should be noted a Debug/DU build can be used in place of Edittime/EU or Runtime/RU.

MakePDBDuringReleaseBuild = true|false
If this setting is false (default):

  • Debug/DU: full PDB built, allowing best debugging experience.
  • Edittime/EU and Runtime/RU: no PDBs built.

If this setting is true:

  • Debug/DU: full PDB built, allowing best debugging experience.
  • Edittime/EU, Runtime/RU: both stripped and full built, increasing the MFX size mildly.

PDBs allow an end user to debug with Visual Studio. You can safely distribute the stripped PDB without giving away your source code. A full PDB will have a name like "ExtName_Full.pdb", a stripped one will be "ExtName.pdb".

Disable Windows XP compatibility

WindowsXPCompatibility = true|false
If true, it will cause Windows XP+ compatible compiler.
If false, it will cause a fancy Vista+ compiler to be used instead.
By default, it is true, if the Windows XP compatibility toolset is installed.

Run static code analysis on build

RunStaticCodeAnalysis = true|false|debugonly
If true, forces toolset to non-XP, and enables other static analysis settings, so Code Analysis will run during build, after all files are compiled.
If the setting is "debugonly", those changes will only apply during Debug and Debug Unicode builds.

For advanced C++ developers, you can add more static code analysis annotations to your project, so the analysis tool can look for more subtle mistakes (e.g. a missing error check), and understand your code better. Look into SAL code annotation, but be aware that it's MSVC compiler and so the annotations will not compile outside of Windows builds.
It is recommended to not use static code analysis constantly if you're on Windows XP, but as a pre-release run.

Use multiprocessor compilation in debug builds

UseMultiProcessorCompilationInDebug = true|false
False by default. Makes multiple compiler processes be run for a single project compilation, using all of the CPU during compilation.

This is incompatible with Edit And Continue debugging, so it is off by default in debug builds, but enabled in release builds. If you are not using Edit and Continue, you can enable multiprocessor compilation.

It should be noted that if you're compiling multiple projects/project configurations, with multiprocessor off, you will still get multiple compiler processes running, one for each project + project configuration.

Use link-time code generation

UseLinkTimeCodeGeneration = true|false
Default false. Only affects release builds (Edittime/EU, Runtime/RU).

When set to true, enables Whole Program Optimisation/Link Time Code Generation on the destination projects. This will further optimise the code's speed, possibly making it larger by speed improvements, or smaller by removing redundant code.

Most effective when multiple libraries are in use by your extension.

Make the MFX smaller

FavorSizeOrSpeed = speed|size|size_redist
Note this setting only applies to release builds (Edittime/EU, Runtime/RU).

  • If set to "speed", the code is set to be larger, and faster. This is the default.
  • If set to "size", the code is set to be smaller and slower.
  • If set to "size_redist", the code is set to be even smaller, but slower, and have a dependency on VS Runtime Redistributable.

The VS Redistributable you need is based on your Visual Studio version, x86 variant:

  • VS 2019: Redistributable is here in downloads under Other Tools, or download directly here.
  • VS 2017: Redistributable is here under Other Tools, or download directly here.

These settings resulted in MFX file sizes of:

DarkEdif Template Edittime Runtime Bluewing Client Edittime Runtime
speed 311,808 230,400 speed 909,824 846,848
size 275,456 218,112 size 835,072 784,896
size_redist 90,624 40,448 size_redist 512,512 461,312

The numbers are MFX size, in bytes.

Echo all defines from props file during build

Only necessary during FusionSDK.props file debugging, so not for ext devs.
EchoAllDefinesFromPropsFileDuringBuild = true|false
If true, dumps the FusionSDK.props's #defines (the /D commandlines) into Output window.