Skip to content

Embedding resources in your plugin

TiberiumFusion edited this page Oct 15, 2022 · 6 revisions

Back to the Developing with Visual Studio overview article.


This section explains how to embed resource into your precompiled plugin assembly, using a Visual Studio project. If are not using Visual Studio, you should consult the your IDE's manual for how to do this.

For a complete example that uses embedded resources in a meaningful way, please refer to the CompleteWeaponDemo example plugin.


Why embed resources?

If you want to make a plugin that adds new items, weapons, NPCs, etc to Terraria, you will need sprites and maybe sound effects. These images and audio files can be stored inside your precompiled plugin assembly as embedded resources, which your plugin code can then extract and use.

You must precompile your plugin assembly in order to embed resources. If you are not already set up to precompile your plugin assembly, please refer to the article Precompiling your plugin for more information.

How to embedding resources with Visual Studio

This is very simple. Add a file to your project, then set its build action to Embedded Resource.

Demonstration

Let's say you're creating a plugin that adds a new kind of projectile to Terraria, and you want to use this animated sprite:

Projectile sprite image

This image is from the CompleteWeaponDemo example plugin.

To start, open the Visual Studio project for your plugin. If you don't have a Visual Studio project set up to precompile your plugin, please refer to the Creating a VS project for your plugin article.

◆ In the Solution explorer, right-click on your project and create a new folder. Enter a suitable name, like Assets or Resources.

Step 1 img

Step 1b img

◆ Right click on the folder and choose Add -> Existing Item..., then browse to the file you want to add.

Step 2 img

Step 3 img

◆ Select the file you added, then in the Properties window, change its Build Action to "Embedded Resource".

Step 4 img

Step 5 img

And that's it. The next time you build your project, the file will be embedded into your plugin's compiled assembly.

How to access embedded resources in your plugin's code

To read the embedded resource from your plugin's assembly, use the GetPluginAssemblyResourceBytes method that's provided by the HPlugin base type.

Example:

byte[] projectileImageBytes = GetPluginAssemblyResourceBytes("CoolTerrariaDude.Plugins.EmbeddedResourcesDemo.Assets.projectile.png");

NOTE: GetPluginAssemblyResourceBytes() is an instance method that is provided by the HPlugin base type. In other words, any static patch methods you write will not have access to GetPluginAssemblyResourceBytes(). You should load embedded resources in your plugin's Initialize(), ConfigurationLoaded(), or PrePatch() methods.

The string that you provide to GetPluginAssemblyResourceBytes() is the fully-qualified name of your embedded resource. You can determine the fully-qualified name of an embedded resource like so:

  1. Start with the default namespace that's set in your project's Properties. For example, CoolTerrariaDude.Plugins.EmbeddedResourcesDemo.
  2. Add the folder structure (as shown in the Solution Explorer) that contains the file. For example, CoolTerrariaDude.Plugins.EmbeddedResourcesDemo + Assets.
  3. Add the name of the file itself. For example, CoolTerrariaDude.Plugins.EmbeddedResourcesDemo + Assets + projectile.png.

Once you have the byte[] that constitutes your embedded resource, you can turn it into whatever kind of asset object you need. For example, if your asset is an image, you should use CreateTexture2DFromImageBytes() to create a Texture2D from the image's bytes. For more information on converting byte arrays to usable asset objects, please refer to the Helper methods – AssetHandling helpers article.

Idiosyncrasies

  • GetPluginAssemblyResourceBytes() is an instance member and thus cannot be accessed in a static context (i.e. your plugin's static stub patch methods).
  • Creating XNA objects that contain assets, like Texture2Ds, often requires a GraphicsDevice. The only GraphicsDevice you should use is Terraria's GraphicsDevice, which should only be accessed in a stub patch method that runs sometime after Terraria.Main.Initialize().

To handle these limitations, you should read all the byte[]s of your plugin's assets in either Initialize(), ConfigurationLoaded(), or PrePatch(), then create actual assets from them (like Texture2Ds) in a stub patch method.

Example:

private static byte[] projectileImageBytes; // Raw bytes of the image
private static Texture2D projectileTexture; // Usable Texture2D created from the raw bytes
public override void ConfigurationLoaded(bool successfulConfigLoadFromDisk)
{
	projectileImageBytes = GetPluginAssemblyResourceBytes("CoolTerrariaDude.Plugins.EmbeddedResourcesDemo.Assets.projectile.png");
}
public override void PrePatch()
{
	CreateHPatchOperation("Terraria.Main", "LoadContent", "CreateMyPluginAssets", HPatchLocation.Prefix);
}
public static void CreateMyPluginAssets(Terraria.Main __instance)
{
	projectileTexture = HHelpers.AssetHandling.CreateTexture2DFromImageBytes(projectileImageBytes, __instance.GraphicsDevice, true);
		// Parameter doPremultiply is set to true because this image is non-premultiplied and contains partial transparency
}

Alternatively, because the plugin framework only creates 1 instance of your plugin, you could create a static singleton reference to your plugin's instance, which will then be visible to your stub patch methods.

Example:

private static DemoPlugin Singleton; // DemoPlugin is type of this class
public override void Initialize()
{
	Singleton = this;
	/* other code */
}
public override void PrePatch()
{
	CreateHPatchOperation("Terraria.Main", "LoadContent", "CreateMyPluginAssets", HPatchLocation.Prefix);
}
private static Texture2D projectileTexture;
public static void CreateMyPluginAssets(Terraria.Main __instance)
{
	byte[] projectileImageBytes = Singleton.GetPluginAssemblyResourceBytes("CoolTerrariaDude.Plugins.EmbeddedResourcesDemo.Assets.projectile.png");
	projectileTexture = HHelpers.AssetHandling.CreateTexture2DFromImageBytes(projectileImageBytes, __instance.GraphicsDevice, true);
		// Parameter doPremultiply is set to true because this image is non-premultiplied and contains partial transparency
}

Full plugin example with embedded resources

The CompleteWeaponDemo example plugin is a complete demo of how to use embedded resources in a meaningful way.


Next

Read the Source code debugging article to learn how to debug your plugin's code with Visual Studio's debugger.