Marmalade Engine

Packages

[DRAFT] Plugin API

Each API typically has its own structure. This can be accessed from the main EngineAPI structure, and is referenced by a forward declaration in plugin.h.

This means it only has to be imported if that API is needed. Each API should also include an init() function for setting the function registry.

There are two types of structures: Engine-provided and Plugin-provided.

These are tagged with the ENGINE_PROVIDED and PLUGIN_PROVIDED macros respectively.

Initialisation

struct EngineAPI {
    int (*GetVersion)();
    struct Logger (*GetLogger)();
    
    struct ProjectBrowserAPI *ProjectBrowserApi;
    // Other API structures...
};

typedef void (*PluginInitFunc)();
typedef void (*PluginMainFunc)(struct EngineAPI engineApi);

Inside plugin:

#include <plugin.h>

PLUGIN_EXPORT void PluginMain(struct EngineAPI engineApi) {
    // Plugin code here
    struct ProjectBrowserAPI projectBrowserApi = engineApi.ProjectBrowserApi;
    projectBrowserApi.Init();
    projectBrowserApi.RegisterFileType(...);
    // Other API stuff
}

Function Registries

To avoid having to link the engine at run time, the engine API provides a function pointer registry which can be obtained at run-time.

Each API structure usually has its own function pointer registry, meaning they only have to be included if the API is used by the plugin.

Function registries are typically used by internal functions, such as default implementations. In the example below, the registry is used to provide the MatchByExtension and DefaultRename functions. MatchByExtension is used by the MATCH_BY_EXTENSION helper macro, and isn't designed to be called directly by the user.

Other functions APIs provide are usually in the top-level API structure.

struct ProjectBrowserFunctionRegistry {
    int (*MatchByExtension)(void *args);
    int (*DefaultRename)(void *item);
    // ... other project browser functions
};

struct ProjectBrowserAPI {
    void (*Init)();
    struct ProjectBrowserFunctionRegistry*(*GetRegistry)();
};

Internals

Every API structure has an Impl class:

class EngineApiImpl {
    public:
        static int GetVersion();
        static void Print(char *str);
    };

    class ProjectBrowserApiImpl {
    public:
        static void Init();
        static struct ProjectBrowserFunctionRegistry *GetRegistry();

        static int MatchByExtension(void *args);
        static int DefaultRename(void *item);
    };

These function pointers are set by the engine:

void setup() {
    EngineAPI api{};
    api.GetVersion = &EngineApiImpl::GetVersion;

    static auto projectBrowserApi = ProjectBrowserAPI{
            .Init = &ProjectBrowserApiImpl::Init,
            .GetRegistry = &ProjectBrowserApiImpl::GetRegistry};
    api.ProjectBrowserApi = &projectBrowserApi;
}

int Marmalade::ProjectBrowserApiImpl::MatchByExtension(void* args) {
    LOG_INFO("MatchByExtension");
    return false;
}

int Marmalade::ProjectBrowserApiImpl::DefaultRename(void* item) {
    LOG_INFO("DefaultRename");
    return false;
}

void Marmalade::ProjectBrowserApiImpl::Init() {
    LOG_INFO("Project browser init");
}

struct ProjectBrowserFunctionRegistry* Marmalade::ProjectBrowserApiImpl::GetRegistry() {
    static auto funcs = ProjectBrowserFunctionRegistry{
            .MatchByExtension = &ProjectBrowserApiImpl::MatchByExtension,
            .DefaultRename = &ProjectBrowserApiImpl::DefaultRename
    };
    return &funcs;
}

APIs

[DRAFT] Plugin Signing

To ensure the integrity of packages and plugins, plugin signing can be used.

[DRAFT] Project Browser API

File Type Registrations

Allows custom file types to be registered.

This will allow developers to display thumbnails, and custom context menu items to file types.

For example, the ItemRenameFunc could be used to internally change the name of an item when it is renamed in the project browser.

FileTypeCategory is a simple system for categorising file types. It is used to check if a file type is valid, e.g. in dragging and dropping of textures to make sure it is an image.

#define MATCH_BY_EXTENSION(ext)                                                  \
    .MatchFunc = &engineApi.ProjectBrowserApi->GetRegistry()->MatchByExtension, \
    .MatchFuncArgs = &(struct MatchByExtArgs) { ext }

struct FileTypeCategory {
    char* Name;
    char* Description;
};

PLUGIN_PROVIDED struct FileRegistration {
    char* Name;
    char* Description;

    MarmResult (*MatchFunc)();
    void* MatchFuncArgs;

    char* (*DisplayNameFunc)();

    MarmResult (*LoadThumbnailFunc)();

    MarmResult (*ContextMenuItemsFunc)();
    MarmResult (*ItemRenameFunc)();
    MarmResult (*ItemDeleteFunc)();
    MarmResult (*ItemClickFunc)();

    void (*FreeItemFunc)();

    struct FileTypeCategory** categories;
};

There are macros provided for convenience, for example, a file registration could be built with the following functions:

FileRegistration reg = {
    .name = "PNG",
    MATCH_BY_EXTENSION("png"),
    .display_name_func = &png_display_name,
    .load_img_func = &load_png_preview,
    DEFAULT_FILE_ACTIONS,
    .free_item = &free_png,
};

Project Browser GUI

Adding items to Project Browser GUI.

[DRAFT] Communication API

Package-Plugin Communication (PPC)

Inter-Plugin Communication (IPC)

[DRAFT] Licensing API

[!IMPORTANT]
The Licensing API is only available to developers who have a good reason to use it and have been vetted by us.

Usage of the Licensing API without undergoing the vetting process will cause the plugin to crash.

The Licensing API allows the use of DRM and licensing features to protect code and revenue.

The rules for using the Licensing API are very strict.

It can only be used for:

It cannot be used to:

Developers must specify exactly what information is used for licensing, such as machine idenfiers.

The plugin must be signed when using the Licensing API. See Plugin Signing.