Assets
Install exact build outputs and test configuration into server or proxy workspaces.
Assets place supplied files inside a process workspace. An AssetSource is a local path or a named
artifact already supplied by the integration or embedding application; it does not download an
arbitrary URL. The asset then chooses the destination and replacement behavior.
The example below registers a packaged plugin with Gradle, then installs that exact file through a
workspace plan. An embedding application supplies named files through EngineOptions instead.
Register the packaged JAR
For a single-project plugin, add to build.gradle.kts:
anvil {
artifact("plugin-under-test", tasks.named("jar"))
}If your deployable plugin is produced by another task, register that task's single deployable JAR
instead. In a multi-project build, artifact("plugin-under-test", project(":plugin")) references the
plugin subproject. A published support plugin can use a Maven coordinate:
anvil {
artifact("support-plugin", "com.example:support-plugin:1.2.3")
}The support coordinate is a placeholder for an artifact you actually publish or consume. Each registered artifact must resolve to exactly one file.
Attach it to the server
In PaperScenario.define() from the first test, add
these imports:
import me.whereareiam.anvil.api.model.workspace.AssetSource;
import me.whereareiam.anvil.api.model.workspace.WorkspaceAsset;
import me.whereareiam.anvil.api.model.workspace.WorkspacePlan;
import java.nio.file.Path;Add this call to the MinecraftServer builder before .build():
.workspace(WorkspacePlan.builder()
.asset(WorkspaceAsset.builder()
.group("plugin-under-test")
.source(AssetSource.artifact("plugin-under-test"))
.target(Path.of("plugins", "plugin-under-test.jar"))
.build())
.build())Run ./gradlew anvilTest. The artifact's build task runs before the test, and Anvil copies its output
into this server's plugins directory. The JAR must already contain the descriptor and implementation
required by the selected platform. Anvil does not turn an arbitrary Java library into a plugin.
Confirm loading with your plugin's startup output or an application behavior assertion.
Copy fixture files
Use AssetSource.path(path) for a local file or directory. A file source copies to the target file;
a directory source copies its contents into the target directory. For example, to seed a plugin
configuration stored at src/anvil/fixtures/config.yml, use that source path and target
Path.of("plugins", "MyPlugin", "config.yml") in another asset.
Write a generated file
Use AssetSource.text(content) when the scenario builds the file itself, for example a configuration
that depends on the scenario's settings. Anvil writes the text to the target as a UTF-8 file, so the
scenario needs no temporary file:
WorkspaceAsset.builder()
.source(AssetSource.text("motd: Anvil\n"))
.target(Path.of("plugins", "MyPlugin", "config.yml"))
.build();Anvil writes the text as given. Produce structured formats such as YAML or JSON with a serializer rather than by joining strings, so that values are quoted and escaped correctly.
The default AssetInstallMode.ALWAYS replaces the target when workspace preparation runs. For a
directory source, replacement removes the existing target directory first, so files absent from the
source are not preserved. Choose SEED_ONCE when existing generated state should survive; see
persistent workspaces.
Targets must stay below the workspace. Absolute paths, traversal, symbolic-link paths, and overlapping asset targets are rejected. Put separate assets at distinct targets.
Respect generated runtime settings
Anvil restores caches and installs assets before platform configuration. Platform-owned runtime settings such as ports, agent credentials, forwarding, and EULA acceptance take precedence over copied configuration. Keep your application settings in the asset and let Anvil assign the runtime values.
For a server executable, use Distribution.artifact(name) instead of copying it as a plugin asset;
see distribution selection.