Skip to content

Plugins ​

This guide explains how to build and develop the C++ plugins that provide core functionality for the micromissiles simulator and allow C++ code to be executed within Unity.

Prerequisites ​

  • A C++ compiler (GCC, Clang, or MSVC)
  • Git
  • Python (for Bazel)
  • Bazel 8 (or Bazelisk)

Installing Bazel ​

This project requires Bazel version 8.

For detailed installation instructions for your platform (Windows, macOS, Linux), please refer to the official Bazel documentation.

A few important notes:

  • Bazelisk is recommended as it automatically manages Bazel versions based on the .bazelversion file.
  • On Windows, you will need additional components like MSYS2 and Visual Studio Build Tools.
  • You can verify your installation with bazel version.

Building the Plugins ​

  1. Change into the plugins directory.

    bash
    cd plugins
  2. Build all plugins:

    bash
    bazel build //...
  3. Build specific plugins:

    bash
    # Build the assignment plugin.
    bazel build //:assignment
    
    # Build the example plugin.
    bazel build //:example
    
    # Build and package all plugins into a tarball.
    bazel build //:plugins
    
    # Build and package all plugins into a tarball with optimization enabled.
    bazel build -c opt //:plugins

    After building, the compiled shared libraries can be found in the bazel-bin directory. The packaged plugins tarball are located at bazel-bin/plugins.tar.gz.

  4. Run tests:

    bash
    bazel test //...

Integrating with Unity ​

To use these plugins with the Unity project:

  1. Build the plugins using the instructions above. Ensure that you have optimization enabled to reduce the shared library size.
  2. Extract the contents of bazel-bin/plugins.tar.gz.
  3. Copy the shared libraries (.dll, .dylib, or .so files) to the appropriate plugins directory in the Unity project.

Ubuntu Compatibility ​

Ensure that the plugin is compiled on the same Ubuntu version (e.g., 2022.04) on which the Unity project will run to maintain compability with the glibc and libstdc++ standard libraries. Unity will fail load the plugin if the plugin was compiled against a newer version of glibc or libstdc++ that is not present on the current system.

Currently, the plugin is compatible with Ubuntu 22.04 or newer.

Development ​

Project Structure ​

All plugin-related code can be found under the plugins/ directory. Currently, there are two plugins:

  • assignment/: Implements different assignment algorithms using Google's OR-Tools library to assign interceptors to threats.
  • protobuf/: Implements loading Protobuf messages from Protobuf text files.

Other useful code can be found in the other directories:

  • base/: Common base utilities.
  • example/: Contains an example plugin implementation.
  • experimental/: Experimental code, including toy examples to demonstrate various packages.

Status Codes ​

Plugins should never throw exceptions as these cannot be caught by Unity and will cause the simulation to crash. Instead, plugins should always return a status code that Unity can check and handle appropriately.

The complete list of status codes is defined in status.proto and is adapted from Google's Abseil status codes. Defining a Protobuf enumeration for status codes allows both the C++ plugins and the Unity C# code to reference the same set of values.

The most common status codes are given in the table below:

Status CodeDescription
STATUS_OKReturned on success and indicates no error.
STATUS_INVALID_ARGUMENTReturned when an invalid argument was specified.
STATUS_NOT_FOUNDReturned when a resource was not found.
STATUS_FAILED_PRECONDITIONReturned when the arguments are valid but an invariant has not been satisfied.
STATUS_OUT_OF_RANGEReturned when a resource was accessed past its valid range.
STATUS_UNIMPLEMENTEDReturned when the operation has not been implemented.
STATUS_INTERNALReturned when an internal error occurred during the operation.

For more details on the meaning and usage of each status code, refer to the Abseil status documentation.

Example ​

Because plugins are designed to return a status code rather than throw exceptions, they rely on output arguments to pass data back to Unity. For example, the Assignment plugin exposes the following C API for performing an even assignment:

cpp
// Assign the agents to the tasks using an even assignment.
plugin::StatusCode Assignment_EvenAssignment_Assign(
    const int num_agents, const int num_tasks, const float* costs,
    int* assigned_agents, int* assigned_tasks, int* num_assignments)

The corresponding Unity C# declaration is:

cs
// Assign the agents to the tasks using an even assignment.
[DllImport("assignment")]
public static extern Plugin.StatusCode Assignment_EvenAssignment_Assign(
    int numAgents, int numTasks, float[] costs, int[] assignedAgents, int[] assignedTasks,
    out int numAssignments);

In this example:

  • costs is a float array passed from Unity to the plugin.
  • assigned_agents/assignedAgents and assigned_tasks/assignedTasks are integer arrays used as output arguments.
  • num_assignments/numAssignments is an integer output argument of the plugin.
cs
// Solve the assignment problem.
int[] assignedInterceptorIndices = new int[assignableInterceptors.Count];
int[] assignedThreatIndices = new int[assignableInterceptors.Count];
int numAssignments = 0;
Plugin.StatusCode status = Assignment.Assignment_EvenAssignment_Assign(
    assignableInterceptors.Count, activeThreats.Count, assignmentCosts,
    assignedInterceptorIndices, assignedThreatIndices, out numAssignments);

After the call, the returned status code should always be checked and logged if necessary.

cs
if (status != Plugin.StatusCode.StatusOk) {
  Debug.Log($"Failed to assign the interceptors to the threats with status code {status}.");
  return assignments;
}

Released under the BSD-3-Clause License.