Import Functions

Learn how to import functions from other plugins written in different languages and use them in your own.

To use functions from another plugin in your Go plugin, you need to generate language-specific header files. These headers provide the necessary wrappers to call functions exported by other plugins. This guide explains how to generate these headers and how to use them in your Go plugin.

Generating Header Files

Plugify provides a unified generator tool to automatically generate header files for importing functions from other plugins. These headers include wrapper functions that handle the function calls and parameter passing.

Steps to Generate Bindings

Using the Online Generator:

Visit plugify-gen tool to generate bindings through a user-friendly web interface. Simply upload your plugin manifest file (.pplugin) and select Go as the target language to generate the corresponding Go module.

Using the Command-Line Tool:

You can also download and use the generator tool locally from the plugify-gen repository.

Example usage:

plugify-gen -manifest ./plugins/MyPlugin/MyPlugin.pplugin -output ./output/ -lang golang

Integrate the Generated Module:

  • The tool will generate a package folder (e.g., plugin_from_another_language/) and a file plugin_from_another_language.go next to it in the specified output directory.
  • Copy the package folder to your project directory. The separate plugin_from_another_language.go file is only needed for the single-runtime mode of the Go module; a regular plugin built with -buildmode=c-shared does not use it.
  • The package is imported as a local Go package of your module.

Project Integration

After generating the module, integrate it into your Go project:

Copy Module to Project

Copy the generated module folder to your project directory:

cp -r ./output/plugin_from_another_language ./my_plugin/

Import the Module

Import the generated package in your Go files:

import (
    "my_plugin/plugin_from_another_language"
)

Update go.mod (if needed)

The import path starts with your module name from go.mod, so no replace is needed. The generated package uses go-plugify, which your plugin already requires:

go.mod
module my_plugin

go 1.27

require github.com/untrustedmodders/go-plugify v1.10.6

Using Generated Wrapper Functions

The generated module contains wrapper functions that allow you to call functions from the other plugin. These wrappers handle the function address lookup and parameter passing.

Example Generated Header

Here’s an example of a generated header file for a plugin named plugin_from_another_language:

plugin_from_another_language/core.go
package plugin_from_another_language

/*
#include "core.h"
*/
import "C"
import (
    "unsafe"
    "github.com/untrustedmodders/go-plugify"
)

var _ParamCallback = func(a int32, b float32, c float64, d plugify.Vector4, e []int64, f int8, g string, h uint16, k int16) {
    __a := C.int32_t(a)
    __b := C.float(b)
    __c := C.double(c)
    __d := *(*C.Vector4)(unsafe.Pointer(&d))
    __e := plugify.ConstructVectorInt64(e)
    __f := C.int8_t(f)
    __g := plugify.ConstructString(g)
    __h := C.uint16_t(h)
    __k := C.int16_t(k)
    plugify.Block {
        Try: func() {
            C.ParamCallback(__a, __b, __c, &__d, (*C.Vector)(unsafe.Pointer(&__e)), __f, (*C.String)(unsafe.Pointer(&__g)), __h, __k)
        },
        Finally: func() {
            // Perform cleanup.
            plugify.DestroyVectorInt64(&__e)
            plugify.DestroyString(&__g)
        },
    }.Do()
}

// ParamCallback 
func ParamCallback(a int32, b float32, c float64, d plugify.Vector4, e []int64, f int8, g string, h uint16, k int16) {
    _ParamCallback(a, b, c, d, e, f, g, h, k)
}
plugin_from_another_language/core.h
#pragma once

#include "shared.h"

extern void (*__plugin_from_another_language_ParamCallback)(int32_t, float, double, Vector4*, Vector*, int8_t, String*, uint16_t, int16_t);

static void ParamCallback(int32_t a, float b, double c, Vector4* d, Vector* e, int8_t f, String* g, uint16_t h, int16_t k) {
    __plugin_from_another_language_ParamCallback(a, b, c, d, e, f, g, h, k);
}

How It Works

  • The wrapper function (ParamCallback) handles parameter marshaling and cleanup.
  • The __plugin_from_another_language_ParamCallback function pointer is set by the language module during plugin load.
  • The wrapper function ensures that Go types are correctly converted to C types and vice versa.

Example: Using the Generated Header

Here’s how you can use the generated package in your Go plugin. Add the other plugin to the dependencies of your manifest, so that it is loaded first and the function pointers are set before your plugin starts:

plugin.go
package main

import (
    "fmt"

    "github.com/untrustedmodders/go-plugify"
    "my_plugin/plugin_from_another_language"
)

func onPluginStart() error {
    // Call the exported function from the other plugin
    plugin_from_another_language.ParamCallback(
        42,                // int32 a
        3.14,              // float32 b
        2.718,             // float64 c
        plugify.Vector4{X: 1, Y: 2, Z: 3, W: 4}, // vec4 d
        []int64{100, 200}, // []int64 e
        'x',               // int8 f (char8)
        "Hello, Plugify!", // string g
        '✓',               // uint16 h (char16)
        10,                // int16 k
    )
    fmt.Println("ParamCallback called")
    return nil
}

func init() {
    plugify.NewPlugin("my_plugin", onPluginStart, nil, nil)
}

func main() {}

When is Header Generation Necessary?

Header generation is essential when importing functions from plugins written in statically-typed languages like C++ or Go. Without these headers, the compiler cannot reference the exported functions. For dynamically-typed languages like Python, header generation is not necessary because method binding happens at runtime.

Best Practices

  1. Use the Generator Tool: Always use the Plugify generator tool (online or command-line) to generate headers for imported functions.
  2. Include Generated Headers: Include the generated headers in your plugin source files to access the exported functions.
  3. Test Thoroughly: Test the imported functions to ensure they work as expected.
  4. Document Dependencies: Clearly document the plugins and functions your plugin depends on.

Conclusion

Importing functions from another plugin in Go is straightforward when you use the Plugify generator tool to generate the necessary headers. These headers provide wrapper functions that handle function address lookup and parameter passing, making it easy to integrate functionality from other plugins. By following the steps and best practices outlined in this guide, you can create robust and interoperable plugins in the Plugify ecosystem.