Integrating

How to embed Plugify into your own projects.

Plugify is designed to be easily integrated into your projects, whether you're building a game modding platform, a standalone application, or a plugin-based system. This guide will walk you through the process of integrating Plugify into your project using CMake, covering both external and embedded integration methods.

Integration Methods

1. External Integration

To use Plugify as an external library, you can locate it directly with find_package() in your CMake project. This method is ideal if you want to keep Plugify as a separate dependency.

Example:

CMakeLists.txt
find_package(plugify REQUIRED)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE plugify::plugify)

2. Embedded Integration

If you prefer to embed Plugify directly into your project, you can include the entire source tree as a subdirectory. This method is useful if you want to customize or extend Plugify's functionality.

Example:

CMakeLists.txt
# Disable tests for third-party libraries
set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "")

# Add Plugify as a subdirectory
add_subdirectory(plugify)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE plugify::plugify)

3. Embedded Integration with FetchContent

For projects using CMake 3.11 or later, you can use the FetchContent module to automatically download and integrate Plugify at configure time.

Example:

CMakeLists.txt
include(FetchContent)

FetchContent_Declare(
plugify
URL https://github.com/untrustedmodders/plugify/releases/download/v1.0.0/plugify.tar.xz
)
FetchContent_MakeAvailable(plugify)

target_link_libraries(foo PRIVATE plugify::plugify)

Note: The URL approach is recommended for stable releases. For more details, see the FetchContent documentation.

4. Supporting Both External and Embedded Integration

If you want your project to support both external and embedded integration, you can use the following pattern:

Top-Level

CMakeLists.txt
project(FOO)
...
option(FOO_USE_EXTERNAL_PLUGIFY "Use an external Plugify library" OFF)
...
add_subdirectory(thirdparty)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE plugify::plugify)

Thirdparty

thirdparty/CMakeLists.txt
if(FOO_USE_EXTERNAL_PLUGIFY)
    find_package(plugify REQUIRED)
else()
    add_subdirectory(plugify)
endif()

In this setup, thirdparty/plugify should contain a complete copy of the Plugify source tree.

Example: Initializing Plugify in Your Application

This code creates an instance of the plugify::Plugify object. It sets up services, initializes the instance, and then interacts with a plugin manager. Error handling is included for initialization failures.

main.cpp
// Example 1: Simple usage with defaults
int main() {
    auto result = plugify::MakePlugify("./app");
    if (!result) {
        std::cerr << "Failed to create Plugify: " << result.error().message << std::endl;
        return 1;
    }

    auto plugify = result.value();
    if (auto initResult = plugify->Initialize(); !initResult) {
        std::cerr << "Failed to initialize: " << initResult.error().message << std::endl;
        return 1;
    }

    // Load extensions
    const auto& manager = plugify->GetManager();
    manager->Initialize();

    // Main loop
    bool running = true;
    while (running) {
        plugify->Update();
        // Your application logic here
    }

    plugify->Terminate();
    return 0;
}
main.cpp
// Example 2: Advanced configuration
int main() {
    // Create custom logger
    auto logger = std::make_shared<plugify::impl::AsyncLogger>(
        std::make_shared<plugify::impl::FileLogger>("./logs/app.log")
    );

    // Configure plugin manager
    plugify::Config config;
    config.paths.baseDir = "./app";
    config.paths.extensionsDir = "extensions";
    config.loading.enableHotReload = true;
    config.loading.parallelLoading = true;
    config.runtime.updateInterval = std::chrono::milliseconds(16);

    // Build Plugify instance
    auto result = plugify::Plugify::CreateBuilder()
        .WithConfig(config)
        .WithLogger(logger)
        .WithService<MyCustomService>(std::make_shared<MyCustomServiceImpl>())
        .Build();

    if (!result) {
        std::cerr << "Failed: " << result.error().message << std::endl;
        return 1;
    }

    auto plugify = result.value();

    // Initialize asynchronously
    auto initFuture = plugify->InitializeAsync();

    // Do other initialization...

    // Wait for initialization
    if (auto initResult = initFuture.get(); !initResult) {
        std::cerr << "Failed to initialize: " << initResult.error().message << std::endl;
        return 1;
    }

    // Access service locator for convenient operations
    auto logger = plugify->GetServices().Resolve<ILogger>();
    logger->Log("Application started", Severity::Info);

    // Main loop
    bool running = true;
    while (running) {
        plugify->Update();
    }

    plugify->Terminate();
    return 0;
}

Best Practices

  1. Use Consistent Build Environments:
    • Ensure that Plugify and your project are built with the same compiler and C++ version to avoid compatibility issues.
  2. Disable Unnecessary Features:
    • If you don’t need tests or documentation, disable them using CMake options like PLUGIFY_BUILD_TESTS and PLUGIFY_BUILD_DOCS.
  3. Handle Errors Gracefully:
    • Always check the return values of Plugify's initialization and management functions to handle errors effectively.
  4. Use FetchContent for Stable Releases:
    • When using FetchContent, prefer downloading stable releases from GitHub to ensure compatibility and reliability.

Troubleshooting

  • Missing Packages: If the Package Manager reports missing packages, ensure that the required packages are available in the configured directories or repositories.
  • Conflicted Packages: Resolve conflicts by uninstalling or updating the conflicting packages.
  • Build Errors: Verify that your CMake configuration matches Plugify's requirements (e.g., C++20, compatible compilers).