Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ohemilyy/universe/llms.txt

Use this file to discover all available pages before exploring further.

TemplateVariableProvider lets extensions inject custom placeholder variables into the template replacement pipeline. During instance deployment, Universe scans the files listed in Configuration.fileModifications and replaces every known placeholder with its resolved value. Built-in variables like %PORT% and %INSTANCE_ID% come from the core; extensions contribute additional ones through this interface. All three interfaces live in extensions/api (gg.scala.universe.template).

TemplateVariableProvider interface

interface TemplateVariableProvider {
    fun provideVariables(): Map<String, String>
}
provideVariables() is called once per instance deployment. It returns a Map<String, String> where each key is a placeholder string (conventionally wrapped in %) and each value is the string that will replace it in the modified files.

TemplateVariableRegistry interface

interface TemplateVariableRegistry {
    fun register(provider: TemplateVariableProvider)
    fun collectVariables(): Map<String, String>
}
MethodDescription
register(provider)Adds provider to the set of active variable contributors.
collectVariables()Calls provideVariables() on every registered provider and merges the results into a single map. Later registrations win on key conflicts.

How variables are applied

  1. A new instance is created and its template files are copied to ./running/<instanceId>/.
  2. The Template Manager calls TemplateVariableRegistry.collectVariables() to build the merged placeholder map. Built-in variables are added to this map alongside any extension-provided ones.
  3. For each file listed in Configuration.fileModifications, the Template Manager scans the file content and replaces every matching placeholder string.
A configuration that has "fileModifications": ["server.properties"] will have all known placeholders replaced inside server.properties before the instance process starts.

Implementation example

class MyVariableProvider : TemplateVariableProvider {
    override fun provideVariables(): Map<String, String> {
        return mapOf(
            "%LOBBY_ADDRESS%" to "lobby.example.com",
            "%REGION%" to System.getenv("REGION") ?: "us-east"
        )
    }
}

class MyExtension : Extension {
    @Inject lateinit var variableRegistry: TemplateVariableRegistry

    override fun onLoad() {
        variableRegistry.register(MyVariableProvider())
    }
}
With this extension loaded, any file listed in fileModifications that contains %LOBBY_ADDRESS% will have it replaced with lobby.example.com at deployment time.

TemplateResolver interface

interface TemplateResolver {
    fun resolveTemplates(pattern: String): List<Pair<String, String>>
}
TemplateResolver is provided by the core app module. Extensions can inject it to resolve a user-supplied pattern string into a concrete list of (group, name) pairs.

Supported patterns

PatternResolves to
*All templates in all groups
group/(*)All templates inside the named group
group/nameA single specific template

Usage example

class TemplateSyncCommand {
    @Inject lateinit var resolver: TemplateResolver

    fun sync(pattern: String) {
        val templates = resolver.resolveTemplates(pattern)
        // templates is a List<Pair<String, String>> of (group, name)
        for ((group, name) in templates) {
            // dispatch sync task for each resolved template
        }
    }
}
TemplateResolver is used internally by template sync console commands. Extensions that implement custom sync logic should inject it rather than re-implementing pattern matching.

Build docs developers (and LLMs) love