Skip to content

Plugin Types

Jeff Campbell edited this page Oct 28, 2020 · 1 revision

Plugin Types

Genesis is a tool that enables developers to easily add code-generation capabilities to their ganes, libraries with ease. It does this by exposing several plugin types and configuration types via the Genesis.Editor AssemblyDefinition that allow for hooking into the code-generation at different points in the process.

  • ICacheable: While not a plugin interface itself, if implemented on a code gen plugin it will pass a shared cache to be set prior to the code-generation process beginning on all implementing types. This cache will be shared between all plugins executing from a single GenesisSettings config.
/// <summary>
/// Indicates that the object should be given access to a in-memory, thread-safe cache shared between multiple
/// plugins.
/// </summary>
public interface ICacheable
{
	/// <summary>
	/// Assigns the shared memory cache to this plugin.
	/// </summary>
	void SetCache(IMemoryCache memoryCache);
}
  • IConfigurable: While not a plugin interface itself, all plugin interfaces that implement this will be configured with the GenesisSettings instance for this run prior to the code generation process beginning. This can be useful for using project settings configured by a user in one or more plugins.
/// <summary>
/// Indicates that an object can be configured based on project settings in
/// <see cref="GenesisSettings"/>.
/// </summary>
public interface IConfigurable
{
	/// <summary>
	/// Configures preferences
	/// </summary>
	void Configure(GenesisSettings settings);
}
  • ICodeGenerationPlugin: All other interface types, with exception to ICacheable and IConfigurable derive from this internal interface. This allows for defining the plugin's display name as seen by a user during code generation, the priority this plugin should run with regards to others of the same interface type (ordered from lowest to highest), and whether or not this plugin should execute as part of a Dry Run (essentially a test run before actually generating code).
/// <summary>
/// Represent an object that offers some measure of code generation plugin functionality.
/// </summary>
public interface ICodeGenerationPlugin
{
	/// <summary>
	/// The name of the plugin.
	/// </summary>
	string Name { get; }

	/// <summary>
	/// The priority value this plugin should be given to execute with regards to other plugins,
	/// ordered by ASC value.
	/// </summary>
	int Priority { get; }

	/// <summary>
	/// Returns true if this plugin should be executed in Dry Run Mode, otherwise false.
	/// </summary>
	bool RunInDryMode { get; }
}
  • IPreProcessor: This plugin type allows for logic to be executed prior to any code generation. Good usages of this type include any asset staging, validation, fixes, etc..
/// <summary>
/// Represents a plugin that executes a processing action before all other plugin types.
/// These are commonly used for validation prior to generating code.
/// </summary>
public interface IPreProcessor : ICodeGenerationPlugin
{
	/// <summary>
	/// Executes a pre-processing action.
	/// </summary>
	void PreProcess();
}
  • IDataProvider: This plugin type is used to execute logic searching for and creating CodeGeneratorData instances used as the basis upon which to generate code in ICodeGenerator instances. CodeGeneratorData itself is simply an abstract dictionary allowing you to assign arbitrary data to a string key. Typically these might be populated using reflection to search for desired types and setting various bits of key information per type into an instance of CodeGeneratorData, but you could also infer what to generate code from based on whatever you want.

Genesis's architecture encourages using one IDataProvider for each unique type or category that would cause code to be generated and returns an array of CodeGeneratorData derived instances for all discovered instances. This contrasts with ICodeGenerator, where you may have more then one plugin that generates different types of script files based on the same set of CodeGeneratorData. This implies the relationship of IDataProvider to ICodeGenerator for the same type of data to be One to Zero or More.

For a practical example of this, please look at the included ScriptableFactoryDataProvider and ScriptableFactoryCodeGenerator types for how these responsibilies are broken out between A plugin that provides data and A plugin that generates code based on that provided data.

/// <summary>
/// Represents a plugin that executes before code generation to provides data for
/// <see cref="ICodeGenerationPlugin"/> instances.
/// </summary>
public interface IDataProvider : ICodeGenerationPlugin
{
	/// <summary>
	/// Creates zero or more <see cref="CodeGeneratorData"/> derived instances 
	/// for code generation to execute upon.
	/// </summary>
	/// <returns></returns>
	CodeGeneratorData[] GetData();
}
  • ICodeGenerator: This plugin type is used to execute the logic generating CodeGenFile instances based on the passed superset of all CodeGenerationData instances. Typically this involves filtering that superset to the subset of instances specific to your plugin.

For example, the ScriptableFactoryCodeGenerator filters that collection for all CodeGenerationData derived instances where the type of data is a FactoryKeyEnumData. This allows this custom code generator plugin to generate its code files off of that specific type of code gen data.

How a CodeGenFile is created is largely up to you. ScriptableFactoryCodeGenerator uses token substituion over string formatting because I believe its easier to read and understand the template code file that way, but in short its up to you to decide how you want to constitute the parts of the code file set into a CodeGenFile instance.

/// <summary>
/// Represents an object that can generate zero or more <see cref="CodeGenFile"/> 
/// instances based on a subset of <see cref="CodeGeneratorData"/> instances.
/// </summary>
public interface ICodeGenerator : ICodeGenerationPlugin
{
	CodeGenFile[] Generate(CodeGeneratorData[] data);
}
  • IPostProcessor: This plugin type executes logic after all ICodeGenerator plugins have executed and is passed the superset of all CodeGenFile instances to apply its logic to and return back. This can be useful where there are desired operations to filter or apply logic to all code generated files.

Practical examples of using this plugin type in this project include logging the results of code generation to the console, converting line endings to the desired format for consistency, writing the CodeGenFile instances to disk, etc...

/// <summary>
/// Represents a plugin which performs a post-processing action on a code-generated file.
/// </summary>
public interface IPostProcessor : ICodeGenerationPlugin
{
	/// <summary>
	/// Returns a set of code generated files that have had a processing action 
	/// performed on them.
	/// </summary>
	CodeGenFile[] PostProcess(CodeGenFile[] files);
}
Clone this wiki locally