A Gradle plugin for Hytale mod development that standardizes project setup, manifest generation, validation, local server runs, IDE-friendly decompiled source attachment, and optional hosted Hytale Javadoc injection into generated server sources.
This plugin replaces manual setup tasks such as:
- manual manifest management
- manual server setup
- manual dependency decompilation
- manual IDE source attachment setup
- manually cross-referencing hosted Hytale server Javadocs while browsing decompiled sources
Need a new mod project? Use the template generator.
This Gradle plugin assumes you already have a project and focuses on Hytale-specific Gradle tasks: manifests, assets, server runs, source attachment, diagnostics, and workspace orchestration.
plugins {
id 'java'
id 'com.azuredoom.hytale-tools' version '1.0.45'
}
hytaleTools {
hytaleVersion = '0.+'
// Optional: override the manifest ServerVersion field.
// If unset, dynamic hytaleVersion selectors like '0.+'
// resolve to the concrete server version in manifest.json.
// manifestServerVersion = '>=0.5.0-pre.9 <0.6.0'
manifestGroup = 'com.example.mods'
// Author format: Name, or Name|Email, or Name|Email|Url.
// Separate multiple authors with commas. Email and Url are optional.
// Example: 'Alice|alice@example.com|https://example.com,Bob'
modCredits = 'yourname'
patchline = 'release'
modId = 'examplemod'
mainClass = 'com.example.mods.ExampleMod'
// Optional: declare sub-plugins bundled with this mod (positional style)
subPlugin('MyFeature', 'com.example.mods.feature.MyFeaturePlugin')
// Or use the map style to set any manifest field (Description, Authors, Website, etc.)
// subPlugin([ Name: 'MyFeature', Main: 'com.example.mods.feature.MyFeaturePlugin', Description: 'My feature' ])
// Optional: declare load ordering
// loadBefore 'otherpluginname'
// loadBefore 'anotherplugin', '>=2.0.0'
// Optional: enabled by default. Injects hosted Hytale API docs into
// generated Vineflower server sources for easier IDE browsing.
injectServerJavadocsIntoSources = true
}Then run:
./gradlew setupHytaleDev
./gradlew runServerHaving issues? See Support & Issues.
To enable debugging and hot swap:
./gradlew runServer -Ddebug=true -Dhotswap=trueFor best results:
- Use JetBrains Runtime (JBR) for enhanced class redefinition support.
- Let the plugin resolve the Java toolchain automatically, or set
jbrHomeif you need a specific JBR installation. - Optionally configure
hotswapAgentPathif you want to use an external HotswapAgent jar.
Example with an external HotswapAgent jar:
hytaleTools {
hotSwapEnabled = true
useHotswapAgent = true
hotswapAgentPath = '/path/to/hotswap-agent.jar'
}Or from the command line:
./gradlew runServer -Ddebug=true -Dhotswap=trueVerify your JVM and hot swap setup:
./gradlew hytaleJvmDoctorWhen launching from IntelliJ using Debug, IntelliJ may provide its own debugger agent. The plugin detects an existing JDWP debugger argument and avoids adding a duplicate one.
This plugin supports a clean separation between workspace orchestration and individual mod projects.
com.azuredoom.hytale-workspaceβ applied to the root projectcom.azuredoom.hytale-toolsβ applied to each mod project
root/
βββ settings.gradle
βββ build.gradle
βββ common/
βββ modA/
βββ modB/
rootProject.name = "my-hytale-workspace"
include("common", "modA", "modB")// root build.gradle
plugins {
id 'com.azuredoom.hytale-workspace' version '1.0.45'
}
// If you are getting an issue with Task 'prepareKotlinBuildScriptModel' not found in project ':modX'.
// Add this
subprojects {
tasks.register('prepareKotlinBuildScriptModel') {
dependsOn(rootProject.tasks.named('prepareKotlinBuildScriptModel'))
}
}
hytaleWorkspace {
modProjects = [':modA', ':modB']
// Optional (recommended)
hostProject = ':modA'
// Optional shared defaults propagated to child `hytaleTools` projects
// when those projects apply the plugin, unless overridden locally
manifestGroup = 'com.example.mods'
hytaleVersion = '0.+'
patchline = 'release'
}// common/build.gradle
plugins {
id 'java-library'
}// modA/build.gradle
plugins {
id 'com.azuredoom.hytale-tools'
}
dependencies {
implementation project(':common')
}
hytaleTools {
// hytaleVersion, patchline, and manifestGroup are inherited from the workspace
// Override workspace default
manifestGroup = 'com.example.custom'
// Author format: Name, or Name|Email, or Name|Email|Url.
// Separate multiple authors with commas. Email and Url are optional.
// Example: 'Alice|alice@example.com|https://example.com,Bob'
modCredits = 'yourname'
modId = 'moda'
mainClass = 'com.example.mods.moda.ModA'
}Repeat for additional mod projects (e.g. modB).
**Note for Kotlin users, please ensure you are also adding:
repositories {
mavenCentral()
}to any build.gradle that is calling the kotlin plugin.
- The workspace plugin (
hytale-workspace) only applies to the root project. - Each mod project applies
hytale-toolsindependently. - Non-mod projects (for example,
common) remain standard Gradle modules. - Workspace tasks operate only on projects listed in
modProjects. WhenmodProjectsis omitted, the workspace discovers subprojects that applycom.azuredoom.hytale-tools. - The workspace plugin does not automatically apply the mod plugin.
stageAllModAssetsdepends on each mod project'sclassesandvalidateManifesttasks.- Each workspace mod is staged as a directory under
run/mods/<manifest_group>_<mod_id>/. Its resources and compiled class outputs are linked into that directory, with a file-copy fallback when links are unavailable. runAllModslaunches one server using those staged plugin directories plus a shared runtime classpath forvineImplementationdependencies.- Workspace plugin identifiers must be unique by
manifestGroup:modId.
Available only on the root project:
updateAllPluginManifestsvalidateAllManifestsstageAllModAssetsrunAllMods
Example:
./gradlew updateAllPluginManifests
./gradlew validateAllManifests
./gradlew runAllModsRun a single mod:
./gradlew :modA:runServerRun all mods together:
./gradlew runAllModsThe workspace uses a host project to resolve:
- Hytale assets
- Server runtime configuration
You can explicitly configure it:
hytaleWorkspace {
hostProject = ':modA'
}If not set, the first project (by path) is used.
If hytaleHomeOverride is needed for runAllMods, configure it on the selected host
project's hytaleTools block. The workspace plugin uses the host project's resolved
assets path for the shared server launch.
project(':modA') {
hytaleTools {
hytaleHomeOverride = "/path/to/Hytale/install/release/package/game/latest/Assets.zip"
}
}- All workspace mod projects must use the same configured
hytaleVersionandpatchlineafter workspace defaults and local overrides are applied. runAllModsfails early when versions or patchlines differ, a workspace plugin identifier is duplicated, a resource directory is missing, or no compiled class output exists.- Workspace mods are staged into
run/mods/as development directories containing linked resources and compiled classes; they are not built and copied as project jars for the development run. - Symbolic links are preferred for individual files. On Windows, the plugin also attempts hard links before falling back to copying.
- A copy fallback disables live updates for the copied file until the staging task runs again.
commonshould not apply the plugin unless it is itself a loadable Hytale plugin.- Stale entries created by previous staging runs are tracked with internal index files and removed automatically.
This setup is ideal for:
- shared code (
common) - multiple independent mods
- a single combined development server
The design keeps:
- mod configuration isolated per project
- workspace behavior centralized at the root
Generate recommended VS Code project files:
./gradlew configureVSCodeHytaleRunThis creates:
.vscode/extensions.json.vscode/settings.json.vscode/launch.json.vscode/tasks.jsonwith common Hytale Gradle tasks
For source attachment, run:
./gradlew prepareDecompiledSourcesForIdeTo debug the local Hytale server use the generated launch configuration Hytale: runServer, which launches and sets the server to debug mode.
Generate an optional Dev Container:
./gradlew generateHytaleDevContainerThis creates .devcontainer/devcontainer.json and .devcontainer/Dockerfile.
The Dev Container is optional and mainly useful for contributors who want a reproducible Java/Gradle environment.
The plugin separates compile-time visibility, runtime class loading, plugin discovery, and IDE source generation:
vineServerJar ββββββββββββββββ> compileOnly / compileClasspath
vineCompileOnly ββββββββββββββ> compileOnly / compileClasspath
vineImplementation βββββββββββ> implementation / runtimeClasspath
requiredDependency βββββββββββ> implementation / runtimeClasspath
optionalDependency βββββββββββ> compileOnly / compileClasspath
vineDecompileTargets βββββββββ> IDE decompilation targets
vineMod ββββββββββββββββββββββ> run/mods as a complete plugin jar
vineImplementation plugin jar
(contains manifest.json) βββ> run/mods as a complete plugin jar
ββ> exploded shared runtime classpath
(manifest.json omitted)
vineImplementation library jar
(no manifest.json) βββββββββ> exploded shared runtime classpath only
At a high level:
vineServerJarprovides the Hytale server API and server runtime.vineCompileOnlyis for dependencies needed to compile but not needed for the local runtime.vineImplementationis for dependencies needed at compile time and during local server runs.requiredDependencyis a convenience alias for a required dependency: it is available throughimplementation, participates in dependency decompilation/source attachment, and should correspond to an entry inmanifestDependencies.optionalDependencyis a convenience alias for an optional integration: it is available throughcompileOnly, participates in dependency decompilation/source attachment, and should correspond to an entry inmanifestOptionalDependencies.- A
vineImplementationjar containing a root-levelmanifest.jsonis treated as a Hytale plugin: the complete jar is copied intorun/mods, and its contents are also expanded into an isolated runtime-classpath directory so its classes are visible to the shared JVM classloader. - A
vineImplementationjar withoutmanifest.jsonis treated as a normal library and is expanded onto the shared runtime classpath without being placed inrun/mods. vineModis for a runtime plugin that should be copied intorun/modsas a jar but should not be added to the shared application classpath.vineDecompileTargetscontrols additional dependencies that receive generated source attachment.- Hytale
Assets.zipcan be exposed separately for IDE asset browsing.
The plugin packages the resolved Hytale Assets.zip into a dedicated hytale-assets binary jar and installs it into the local generated repos alongside the server library.
This means:
- assets appear as a separate
hytale-assetsentry under External Libraries in IntelliJ - you can browse game assets independently of server classes in the library tree
- the server library remains clean β no asset entries embedded in it
- assets are not included in your final mod jar
If generateAssetsBinary = false, the plugin still resolves Assets.zip for local runs,
but skips generating the large hytale-assets IDE binary jar.
The plugin provides cleanup tasks for generated Hytale development outputs and caches.
./gradlew cleanHytaleGenerated
./gradlew cleanHytaleAssetsCache
./gradlew cleanHytaleGlobalCacheUse cleanHytaleGenerated when you want to remove project-local generated output, such as generated source jars and generated local source repositories.
Use cleanHytaleAssetsCache when you want to remove the cached Hytale Assets.zip / generated assets cache under the Gradle user home. This is useful when assets look stale, or you need the plugin to resolve them again.
Use cleanHytaleGlobalCache only when you want to clear global Hytale caches under the Gradle user home, such as decompiled source and injected Javadoc caches. Because this can affect other Hytale projects on the same machine, the task requires confirmation.
Interactive confirmation:
./gradlew cleanHytaleGlobalCacheWhen prompted, type:
clean global hytale cache
For non-interactive environments, pass the confirmation property explicitly:
./gradlew cleanHytaleGlobalCache -PconfirmCleanHytaleGlobalCache=trueGenerated VS Code tasks from configureVSCodeHytaleRun include these cleanup commands. The global cache cleanup task asks for confirmation before running.
| Plugin Version | Java Version | Hytale Support |
|---|---|---|
| 1.0.x | 25+ | Release/Pre-Release |
This plugin is designed to work with modern Gradle features:
- β Configuration cache compatible
- β Passes Gradle 9.x plugin validation
β οΈ Selective build cache support
Tasks are categorized as follows:
Globally cached (Gradle user home):
DecompileServerJarTaskβ keyed by patchline + server version under~/.gradle/caches/hytale-decompiled/DecompileDependencyJarTaskβ keyed by Maven coordinates under~/.gradle/caches/hytale-decompiled/dependencies/GenerateAssetsBinaryTaskβ keyed by patchline + server version under~/.gradle/caches/hytale-assets/InjectServerJavadocsIntoDecompiledSourcesTaskβ stamp keyed by patchline + server version under~/.gradle/caches/hytale-javadocs/
These tasks run once per version across all projects sharing the same Gradle user home. Subsequent projects skip the work entirely via onlyIf guards β no Vineflower run, no 3 GiB zip repack.
Not cacheable (by design):
downloadAssetsZip(network + auth dependent)runServer(executes an external process)prepareRunServer(filesystem links/junctions)- manifest tasks (low-cost file operations)
This ensures correctness while still benefiting from caching where it matters.
The plugin is compatible with:
- Gradle configuration cache
- Gradle plugin validation (Gradle 9+)
Recommended CI flags:
./gradlew build --configuration-cache- Automatically adds required repositories
- Creates Hytale-specific and source
vine*configurations - Generates, updates, and validates
manifest.json - Downloads authenticated Hytale assets
- Runs a local Hytale server for development
- Supports debug mode, IntelliJ-friendly JDWP handling, enhanced hot swap setup, and optional external HotswapAgent integration
- Generates decompiled sources and attaches them in IDEs
- Injects hosted Hytale Server API Javadocs into generated Vineflower server sources by default
- Adds external Hytale Server Javadoc links to generated Gradle Javadoc output and IDEA metadata when the
ideaplugin is present
Creates src/main/resources/manifest.json with a default structure if it does not already exist.
This task is safe to run repeatedly and will not overwrite an existing manifest.
Updates (or rewrites) src/main/resources/manifest.json from Gradle configuration.
Resolves the Hytale Assets.zip used by local development.
By default, this task authenticates with Hytale device auth, downloads the asset wrapper,
extracts Assets.zip, and caches the extracted zip under Gradle user home.
If hytaleHomeOverride is configured, the plugin resolves an existing local Assets.zip
from that path and uses it directly. In this mode, the assets zip is not copied into the
Gradle cache.
You can point hytaleHomeOverride at either:
- a direct
Assets.zipfile - a directory containing
Assets.zip - a Hytale launcher/install directory that contains the configured patchline assets
hytaleTools {
hytaleHomeOverride = "/path/to/Hytale/install/release/package/game/latest/Assets.zip"
}or
hytaleTools {
hytaleHomeOverride = "/path/to/Hytale/install/release/package/game/latest/"
}If no override is configured and remote download fails, the task attempts to locate a local
Hytale installation as a fallback and caches the found Assets.zip.
Launches a local Hytale server using:
- the current project's compiled class directories and resource source directories directly
- the project's runtime classpath, excluding the current project output and raw
vineImplementationjars - staged/exploded
vineImplementationruntime-classpath directories - complete
vineModjars inrun/mods - complete
vineImplementationplugin jars inrun/mods - the resolved Hytale server jar
Before launch, prepareRunServer validates the manifest, stages dependencies, and links the configured asset-pack source directory into the run directory.
You can customize the server launch arguments, JVM arguments, debug mode, and hot swap behavior through the extension:
hytaleTools {
hytaleVersion = '0.+'
serverArgs = [
'--allow-op',
'--disable-sentry',
'--disable-file-watcher'
]
serverJvmArgs = [
'-Xms1G',
'-Xmx2G'
]
preRunTask = 'generateDevResources'
// Optional debug / hot swap support
debugEnabled = false
debugPort = 5005
debugSuspend = false
hotSwapEnabled = false
requireDcevm = false
useHotswapAgent = true
// Optional external HotswapAgent jar.
// If unset, the plugin will try bundled JBR HotswapAgent support when available.
hotswapAgentPath = ''
// Optional explicit JetBrains Runtime location
// jbrHome = '/path/to/jbr'
}
tasks.register('generateDevResources') {
doLast {
println 'Preparing additional dev resources...'
}
}You can also enable debug and hot swap from the command line:
./gradlew runServer -Ddebug=true
./gradlew runServer -Ddebug=true -Dhotswap=trueHot swap support depends on the selected JVM:
- Standard JVM: limited to method body changes
- JetBrains Runtime (JBR): supports enhanced class redefinition
- HotswapAgent (if available): improves runtime reload behavior
When hotSwapEnabled is true, the plugin checks the selected JVM for enhanced class redefinition support. If enhanced redefinition is available, it adds -XX:+AllowEnhancedClassRedefinition. If useHotswapAgent is also true, the plugin either uses the configured external hotswapAgentPath jar or falls back to bundled JBR HotswapAgent support when available.
For best results, use JetBrains Runtime with hot swap enabled.
Notes:
serverArgsare appended to the default--assets=...argument automatically added by the pluginserverJvmArgsare added in addition to the pluginβs default JVM launch settingspreRunTasklets you run a custom preparation task beforerunServerdebugEnabledenables JDWP debugging for IDE attachhotSwapEnabledenables runtime probing for enhanced class redefinition supportrequireDcevmfails early if enhanced class redefinition is not availableuseHotswapAgentenables HotswapAgent integration when hot swap is enabledhotswapAgentPathpoints to an external HotswapAgent jar; when set, it is used as-javaagent:<path>=autoHotswap=true- If
hotswapAgentPathis empty, the plugin attempts to use bundled JBR HotswapAgent support when available jbrHomecan be used to point the plugin at a specific JetBrains Runtime installation
Prints JVM diagnostics relevant to debugging and hot swap, including:
- resolved Java executable
- JetBrains Runtime detection
- enhanced class redefinition support
- HotswapAgent mode support
- bundled HotswapAgent availability
This is useful when validating a local JetBrains Runtime setup before using hot swap.
Runtime resolution uses jbrHome first, then known JetBrains Runtime environment variables (JBR_HOME, etc.), and finally falls back to the current JVM.
The plugin can automatically detect JetBrains Runtime installations from common locations, including JetBrains Toolbox installs, when jbrHome is not explicitly configured.
Decompiles the server jar and all dependencies declared in vineImplementation, vineCompileOnly, requiredDependency, optionalDependency, or vineDecompileTargets into build/generated-sources-m2 and build/generated-sources-ivy.
This is useful for IDE source attachment. For the Hytale server jar, the plugin also injects hosted Hytale Server API Javadocs into the generated Vineflower sources by default, so documentation can appear directly in IDE source views instead of only through external Javadoc lookup.
For first-time setup, you can run:
./gradlew setupHytaleDevDuring development:
./gradlew runServerWhat happens during normal development:
updatePluginManifestwrites manifest values from Gradle configuration, if missingcreateManifestIfMissingcreates a default.validateManifestruns beforeprocessResources, ensuring the manifest is generated and checked as part of the build.runServerprepares the run directory, downloads assets if needed, stages plugin dependencies, expands runtime libraries/classes, links the current project's resources, and launches the server.
Because manifest generation and validation are wired into the build, most projects do not need to invoke those tasks manually.
| Property | Type | Default | Required | Purpose |
|---|---|---|---|---|
javaVersion |
Integer |
25 |
No | Java version used for decompilation/tooling |
hytaleVersion |
String |
'0.+' | Yes | Hytale server version to resolve. Accepts dynamic selectors (e.g. 0.+) |
manifestServerVersion |
String |
resolved >=hytaleVersion when unset |
No | Optional manifest ServerVersion override. Supports Hytale semver ranges such as >=0.5.0-pre.9 <0.6.0 |
patchline |
String |
release |
No | Asset/server patchline |
serverJavadocsUrl |
String |
computed from active patchline | No | Hosted Hytale Server API Javadocs URL used for Gradle Javadocs, IDEA metadata, and source injection |
injectServerJavadocsIntoSources |
Boolean |
true |
No | Injects hosted Hytale API docs into generated Vineflower server sources for IDE browsing |
oauthBaseUrl |
String |
Hytale OAuth URL | No | Override auth endpoint |
accountBaseUrl |
String |
Hytale account-data URL | No | Override account endpoint |
hytaleHomeOverride |
String |
empty | No | Optional path to a local Hytale installation, a directory containing Assets.zip, or an Assets.zip file. When set, local dev tasks use this assets zip directly instead of requiring the Gradle cached assets zip. |
generateAssetsBinary |
Boolean |
true |
No | Controls whether prepareDecompiledSourcesForIde / idea generate the large hytale-assets.jar IDE binary from Assets.zip. Disable to avoid creating the extra assets jar. |
manifestGroup |
String |
project.group |
Yes | Manifest group / namespace |
modId |
String |
project.name |
Yes | Manifest mod id |
modDescription |
String |
empty | No | Manifest description |
modUrl |
String |
empty | No | Manifest project URL |
mainClass |
String |
empty | Yes | Plugin entrypoint |
modCredits |
String |
'replace_me' |
No | Manifest authors/credits. Entries use Name, Name|Email, or Name|Email|Url; separate authors with commas. |
manifestDependencies |
String |
Hytale:AssetModule=* |
No | Required manifest deps |
manifestOptionalDependencies |
String |
empty | No | Optional manifest deps |
curseforgeId |
String |
empty | No | CurseForge project id |
disabledByDefault |
Boolean |
false |
No | Manifest flag |
includesPack |
Boolean |
false |
No | Manifest flag |
manifestFile |
RegularFile |
src/main/resources/manifest.json |
No | Manifest location |
runDirectory |
Directory |
run/ |
No | Local server run dir |
assetPackSourceDirectory |
Directory |
src/main/resources |
No | Source asset directory used by runServer and stageAllModAssets |
assetPackRunDirectory |
Directory |
computed under run/mods/... |
No | Assets target dir |
bundleAssetEditorRuntime |
Boolean |
true |
No | Controls whether AssetBridge is bundled into the final jar |
serverArgs |
List<String> |
['--allow-op', '--disable-sentry'] |
No | Additional Hytale server arguments appended after the required --assets=... argument |
serverJvmArgs |
List<String> |
[] |
No | Extra JVM arguments for runServer |
preRunTask |
String |
empty | No | Task name to run before runServer |
debugEnabled |
Boolean |
false |
No | Enables JDWP debugging for runServer |
debugPort |
Integer |
5005 |
No | Debug port used when debugEnabled is true |
debugSuspend |
Boolean |
false |
No | Whether the JVM waits for a debugger before starting |
hotSwapEnabled |
Boolean |
false |
No | Enables hot swap capability detection and runtime setup |
requireDcevm |
Boolean |
false |
No | Fails launch if enhanced class redefinition support is unavailable |
useHotswapAgent |
Boolean |
true |
No | Enables bundled HotswapAgent integration when available |
hotswapAgentPath |
String |
empty | No | Optional path to an external HotswapAgent jar used when hotSwapEnabled and useHotswapAgent are true |
jbrHome |
String |
empty | No | Optional path to a JetBrains Runtime installation |
loadBefore |
List<Map> |
[] |
No | Plugins this mod must be loaded before (see Load Ordering) |
subPlugins |
List<Map> |
[] |
No | Sub-plugins bundled with this mod (see SubPlugins) |
disableHytaleModsInfoMaven |
Boolean |
false |
No | Disables the Hytale-Mods.info Maven repository (see Repositories) |
disablePlaceholderApiMaven |
Boolean |
false |
No | Disables the PlaceholderAPI repository (see Repositories) |
disableCurseMaven |
Boolean |
false |
No | Disables the CurseMaven repository (see Repositories) |
disableAzureDoomMaven |
Boolean |
false |
No | Disables the AzureDoom Maven repository (see Repositories) |
disableHytaleModdingMaven |
Boolean |
false |
No | Disables the Hytale Modding Maven repository (see Repositories) |
disableModtaleResolver |
Boolean |
false |
No | Disables the Modtale API-backed resolver and its modtale alias substitution (see Repositories) |
disableModifoldRepo |
Boolean |
false |
No | Disables the Modifold repository and its modifold alias substitution (see Repositories) |
modCredits is written to the manifest Authors array. Each author supports a required Name and optional Email and Url fields.
Use one of these formats for each author:
NameName|EmailName|Email|Url
Separate multiple authors with commas.
hytaleTools {
modCredits = 'Alice|alice@example.com|https://example.com,Bob,Charlie|charlie@example.com'
}This writes:
{
"Authors": [
{
"Name": "Alice",
"Email": "alice@example.com",
"Url": "https://example.com"
},
{
"Name": "Bob"
},
{
"Name": "Charlie",
"Email": "charlie@example.com"
}
]
}Email and Url are optional. Authors is also optional during validation for compatibility with handwritten or older manifests, but when it is present it must be an array of author objects. Each author object must have a non-blank Name.
Do not use commas or pipe characters inside an individual author value, because commas separate authors and pipes separate author fields.
The plugin supports declaring load-ordering constraints via loadBefore. When set, Hytale will ensure this plugin is loaded before the named plugin(s).
Use the loadBefore DSL method in your hytaleTools block:
hytaleTools {
modId = 'mymod'
mainClass = 'com.example.mods.MyMod'
// Load before any version of another plugin
loadBefore 'otherpluginname'
// Load before a specific version range
loadBefore 'anotherplugin', '>=2.0.0'
}This writes the following into manifest.json:
{
"LoadBefore": [
{
"name": "otherpluginname",
"version": "*"
},
{
"name": "anotherplugin",
"version": ">=2.0.0"
}
]
}If no loadBefore entries are declared, the LoadBefore key is omitted from the manifest entirely.
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
String |
β | The name of the plugin this plugin must be loaded before |
version |
String |
* |
Optional version constraint. Defaults to * (any version) when omitted |
Load ordering can also be declared per sub-plugin by including a LoadBefore list in the sub-plugin map:
subPlugin([
Name : 'Economy',
Main : 'com.example.mods.economy.EconomyPlugin',
LoadBefore : [
[ name: 'otherpluginname', version: '*' ],
[ name: 'anotherplugin', version: '>=2.0.0' ]
]
])Each entry follows the same name / version format as the main manifest. Version defaults to * when omitted.
The plugin supports declaring sub-plugins that are bundled within your main mod. Sub-plugins appear in manifest.json as a SubPlugins array and are loaded by the server alongside the parent mod.
Use the subPlugin(...) DSL method in your hytaleTools block. Two call styles are supported:
Quick shorthand for the most common fields:
hytaleTools {
modId = 'mymod'
mainClass = 'com.example.mods.MyMod'
subPlugin('Forestry', 'com.example.mods.forestry.ForestryPlugin')
subPlugin('Economy', 'com.example.mods.economy.EconomyPlugin', true, false)
// Pin a specific server version for this sub-plugin instead of inheriting the parent's:
subPlugin('Legacy', 'com.example.mods.legacy.LegacyPlugin', false, false, '>=0.5.2')
}| Parameter | Type | Default | Description |
|---|---|---|---|
name |
String |
β | Sub-plugin name (maps to Name in the manifest) |
main |
String |
β | Fully-qualified entry point class (maps to Main) |
disabledByDefault |
boolean |
false |
Whether this sub-plugin is disabled by default |
includesAssetPack |
boolean |
false |
Whether this sub-plugin includes an asset pack |
serverVersion |
String |
null |
Optional server version override. When omitted, the parent mod's manifest ServerVersion is used |
Pass a map of manifest fields directly. This supports all properties that Hytale accepts in a sub-plugin entry β Name, Main, Description, Authors, Website, ServerVersion, DisabledByDefault, IncludesAssetPack, Dependencies, OptionalDependencies, and any future fields the server may support. Name and Main are required; all others are optional.
hytaleTools {
modId = 'mymod'
mainClass = 'com.example.mods.MyMod'
subPlugin([
Name : 'Forestry',
Main : 'com.example.mods.forestry.ForestryPlugin',
Description : 'Adds forestry mechanics to the server',
Authors : [
[ Name: 'Alice', Email: 'alice@example.com', Url: 'https://alice.example.com' ],
[ Name: 'Bob', Email: 'bob@example.com' ]
],
Website : 'https://example.com/forestry',
DisabledByDefault : false
])
subPlugin([
Name : 'Economy',
Main : 'com.example.mods.economy.EconomyPlugin',
Description : 'Optional economy system β enable per server as needed',
Authors : [
[ Name: 'Charlie', Email: 'charlie@example.com', Url: 'https://charlie.example.com' ]
],
DisabledByDefault : true
])
}Any fields not supplied in the map are given safe defaults when written to the manifest (ServerVersion inherits from the parent, DisabledByDefault and IncludesAssetPack default to false).
Each declared sub-plugin is written into manifest.json. The parent manifest ServerVersion is applied automatically to any sub-plugin that does not specify its own:
{
"SubPlugins": [
{
"Name": "Forestry",
"Main": "com.example.mods.forestry.ForestryPlugin",
"Description": "Adds forestry mechanics to the server",
"Authors": [{ "Name": "Alice" }],
"Website": "https://example.com/forestry",
"ServerVersion": ">=0.5.2",
"DisabledByDefault": false,
"IncludesAssetPack": false
},
{
"Name": "Economy",
"Main": "com.example.mods.economy.EconomyPlugin",
"Description": "Optional economy system β enable per server as needed",
"ServerVersion": ">=0.5.2",
"DisabledByDefault": true,
"IncludesAssetPack": false
}
]
}If no sub-plugins are declared, the SubPlugins key is omitted from the manifest entirely.
| Task | Group | Purpose | Typical Use |
|---|---|---|---|
createManifestIfMissing |
hytale |
Creates a starter manifest if missing | First setup |
updatePluginManifest |
hytale |
Rewrites manifest from Gradle config | Normal dev/build |
updateAllPluginManifests |
hytale |
Rewrites manifests for all Hytale subprojects | Multi-project manifest sync |
downloadAssetsZip |
hytale |
Authenticates and fetches assets | Before first run / troubleshooting |
hytaleDoctor |
hytale |
Prints plugin, manifest, asset, and dependency diagnostics | Troubleshooting |
runServer |
hytale |
Launches local Hytale server | Single-project dev loop |
runAllMods |
hytale |
Launches one shared server with all mod subprojects | Multi-project dev loop |
stageAllModAssets |
hytale |
Stages workspace resources/classes and external runtime dependencies | Multi-project run preparation |
prepareDecompiledSourcesForIde |
hytale |
Generates source jars for IDE attachment | IDE setup |
validateManifest |
internal | Verifies generated manifest values | Runs automatically |
validateAllManifests |
hytale |
Verifies manifests for all Hytale subprojects | Multi-project validation |
prepareRunServer |
internal | Stages dependencies and links the project into the run directory | Runs automatically |
decompileServerJar |
internal | Decompiles Hytale server sources | Internal source pipeline |
injectServerJavadocsIntoDecompiledSources |
internal | Injects hosted Hytale API docs into decompiled server sources | Internal source pipeline |
setupHytaleDev |
hytale |
Prepares IDE sources and downloads assets | First-time setup |
cleanHytaleGenerated |
hytale |
Removes project-local Hytale generated outputs | Regenerate IDE/source artifacts |
cleanHytaleAssetsCache |
hytale |
Removes cached Hytale assets / generated assets cache | Refresh stale assets |
cleanHytaleGlobalCache |
hytale |
Removes global Hytale decompile and Javadoc caches | Force full global regeneration |
hytaleJvmDoctor |
hytale |
Prints JVM debug / hot swap diagnostics | Debugging hot swap setup |
The plugin can prepare decompiled sources for IDE use.
It decompiles:
- the Hytale server jar
- every dependency declared in
vineImplementation,vineCompileOnly, orvineDecompileTargets
Generated decompiled sources are packaged as -sources.jar files and installed into local generated repositories under:
build/generated-sources-m2/
build/generated-sources-ivy/The plugin installs both:
- the original binary jar
- the generated sources jar
IDEs use these for source attachment after generation.
Note: These generated repositories are not used for normal dependency resolution during the build. They exist only to support IDE source attachment after sources have been generated.
This avoids Gradle task dependency conflicts and ensures consistent builds.
If the idea plugin is applied, decompiled sources are prepared automatically when you run:
./gradlew ideaYou can also run it directly:
./gradlew prepareDecompiledSourcesForIdeDecompiled source jars are written under:
build/generated-sources-jars/
Use vineDecompileTargets for any dependency whose decompiled sources you want available in your IDE:
dependencies {
vineDecompileTargets 'com.buuz135:MultipleHUD:1.0.6'
}The Hytale server jar does not contain source-level Javadocs, so Vineflower cannot recover comments from bytecode by itself. To make API documentation visible while browsing generated sources, the plugin fetches the hosted Hytale Server API Javadocs and injects matching class, constructor, and method documentation into the generated server source jar.
By default, the Javadocs URL is selected from the configured patchline:
| Patchline | Default Javadocs URL |
|---|---|
release |
https://release.server.docs.hytale.com/ |
pre-release / prerelease |
https://prerelease.server.docs.hytale.com/ |
You can override the URL or disable source injection:
hytaleTools {
// Optional override; normally computed from patchline
serverJavadocsUrl = 'https://release.server.docs.hytale.com/'
// Enabled by default
injectServerJavadocsIntoSources = true
}The injector caches downloaded pages and missing pages under Gradle user home:
~/.gradle/caches/hytale-javadocs/<patchline>/
The first run may take longer while pages are fetched. Later runs are faster because successful pages and missing-page lookups are cached. If hosted docs change, and you need a fresh injection pass, delete the relevant cache directory and rerun:
rm -rf ~/.gradle/caches/hytale-javadocs/release
./gradlew prepareDecompiledSourcesForIde --rerun-tasksThe plugin also adds the hosted Javadocs URL to Gradle javadoc tasks. If the idea plugin is applied, the generated IDEA metadata also receives the external Javadocs URL, but the plugin does not apply idea automatically.
The plugin automatically adds these repositories:
- Maven Central
- Hytale Server Release
- Hytale Server Pre-Release
- Hytale Modding Maven
- Hytale-Mods.info Maven
- PlaceholderAPI
- CurseMaven
- AzureDoom Maven
- Modtale local Maven cache/resolver (exclusive content for group
modtale, populated through the Modtale API) - Modifold (exclusive Ivy content for group
modifold)
You do not need to declare them manually.
Note on patchlines: Both Hytale server repos are registered, but
com.hypixel.hytaleartifacts are only served by the repo matching the configuredpatchline. The other repo is restricted withexcludeGroup('com.hypixel.hytale')so dynamic selectors like0.+never cross patchlines. Other artifacts that happen to live in either repo are unaffected.
If one of the optional mavens is temporarily unreachable and is blocking dependency resolution, you can disable it without waiting for a plugin update:
hytaleTools {
disableCurseMaven = true
}Each optional repository has a matching toggle:
| Repository | Extension property | Gradle property |
|---|---|---|
Hytale-Mods.info Maven |
disableHytaleModsInfoMaven |
hytools.repos.disableHytaleModsInfoMaven |
PlaceholderAPI |
disablePlaceholderApiMaven |
hytools.repos.disablePlaceholderApiMaven |
CurseMaven |
disableCurseMaven |
hytools.repos.disableCurseMaven |
AzureDoom Maven |
disableAzureDoomMaven |
hytools.repos.disableAzureDoomMaven |
Hytale Modding Maven |
disableHytaleModdingMaven |
hytools.repos.disableHytaleModdingMaven |
| Modtale resolver | disableModtaleResolver |
hytools.repos.disableModtaleResolver |
| Modifold | disableModifoldRepo |
hytools.repos.disableModifoldRepo |
Disabling disableModtaleResolver or disableModifoldRepo also disables that group's alias substitution (e.g. modtale:Name_ProjectID:Version resolution), since the substitution has nothing to resolve against once its repository is removed.
The Gradle property form is useful for a global override (e.g. in gradle.properties or CI) without editing every mod project's build.gradle:
hytools.repos.disableCurseMaven=trueMaven Central, Hytale Server Release, and Hytale Server Pre-Release are always added and cannot be disabled, since core plugin functionality depends on them.
The plugin creates Hytale-facing dependency configurations together with internal resolvable configurations used for staging and source generation.
| Configuration | Purpose |
|---|---|
vineServerJar |
Hytale server binary; auto-injected unless you declare one explicitly |
vineImplementation |
Compile/runtime dependency. Plugins are staged into run/mods and the shared classpath; ordinary libraries are shared-classpath only |
vineCompileOnly |
Compile-time dependency that is not staged for local runtime |
requiredDependency |
Alias for a required compile/runtime dependency; extends implementation and participates in decompilation |
optionalDependency |
Alias for an optional compile-time integration; extends compileOnly and participates in decompilation |
vineMod |
Runtime Hytale plugin copied into run/mods as a complete jar, without adding it to the shared classpath |
vineDecompileTargets |
Extra dependencies to decompile for IDE source attachment |
hytaleBundledRuntime |
Runtime dependency automatically added and optionally bundled into the final mod jar |
Internal configurations such as vineImplementationJars, vineModJars, vineDependencyJars, vineDecompileClasspath, and vineflowerTool support resolution, staging, and decompilation. Projects normally declare dependencies using the public configurations above.
requiredDependency and optionalDependency are dependency-bucket aliases. They do not automatically edit manifest.json; you must still declare the matching plugin identifiers in manifestDependencies or manifestOptionalDependencies.
compileOnly automatically includes:
vineCompileOnlyvineServerJarhytaleAssetsfor IDE asset browsing
implementation includes vineImplementation, requiredDependency, and hytaleBundledRuntime.
compileOnly includes vineCompileOnly, optionalDependency, vineServerJar, and hytaleAssets.
Both aliases also feed the decompilation/source-attachment configurations.
This lets you write mods against the Hytale server API without manually declaring the server dependency.
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
maven {
url = uri("https://maven.azuredoom.com/mods")
}
}
}plugins {
id 'java'
id 'com.azuredoom.hytale-tools' version '1.0.45'
}Use the Hytale-specific configurations for plugin and library dependencies:
dependencies {
// Required by your code and by the local development server.
// If the jar contains manifest.json, it is treated as a Hytale plugin.
// Otherwise, it is treated as a normal runtime library.
vineImplementation 'com.example:some-runtime-plugin-or-library:1.0.0'
// Runtime plugin only: copied to run/mods as a complete jar.
// Its classes are not added to the shared launch classpath.
vineMod 'com.example:some-isolated-plugin:1.0.0'
// Required dependency alias: available to compile and run locally.
// Also add its plugin identifier to manifestDependencies.
requiredDependency 'com.example:required-plugin:1.0.0'
// Optional dependency alias: compile-time only for optional integrations.
// Also add its plugin identifier to manifestOptionalDependencies.
optionalDependency 'com.example:optional-plugin:1.0.0'
// Compile-time only: visible to your code, but not staged for a local run.
vineCompileOnly 'curse.maven:hexcodes-1448311:8166165'
// Optional IDE source-attachment target.
vineDecompileTargets 'curse.maven:hexcodes-1448311:8166165'
// Modtale: the text before `_` is an optional readable alias.
// The resolver strips the alias and downloads ProjectID:Version through the Modtale API.
vineImplementation 'modtale:LevelingCore_ProjectID:Version'
// Modifold: the text before `_` is an optional readable alias.
vineImplementation 'modifold:LevelingCore_projectSlug:versionID'
}The plugin provides two convenience configurations for expressing dependency intent in Gradle:
dependencies {
requiredDependency 'com.example:economy-api:1.2.0'
optionalDependency 'com.example:placeholder-api:2.0.0'
}
hytaleTools {
manifestDependencies = 'Example:Economy=*'
manifestOptionalDependencies = 'Example:PlaceholderAPI=*'
}requiredDependency:
- extends
implementation - is available while compiling and running the project
- participates in dependency decompilation and IDE source attachment
- represents a dependency your plugin cannot operate without
optionalDependency:
- extends
compileOnly - is available while compiling optional integration code
- is not automatically placed on the runtime classpath by this alias
- participates in dependency decompilation and IDE source attachment
- represents an integration your plugin can operate without
These aliases describe Gradle classpath behavior only. They do not infer Hytale manifest identifiers from Maven coordinates and do not automatically write Dependencies or OptionalDependencies into manifest.json.
Use vineImplementation when your own code directly references classes from the dependency or when the dependency is a normal library. During development, the plugin determines the jar type by checking for a root-level manifest.json:
- Plugin jar: copied intact to
run/modsfor Hytale plugin discovery and expanded into its own shared-runtime directory, excluding the plugin manifest from that expanded copy. - Library jar: expanded into its own shared-runtime directory only.
- Signature files under
META-INF(.SF,.RSA, and.DSA) are omitted from expanded copies.
Use vineMod when the dependency only needs to be discovered and loaded by Hytale as a plugin and your project does not need its classes on the shared JVM classpath.
Do not declare the same plugin through both configurations. The workspace staging task also rejects different dependencies that resolve to the same filename, because only one file can occupy that path in run/mods.
vineCompileOnly, requiredDependency, and optionalDependency are usually preferred over plain Gradle dependency buckets for Hytale plugin integrations because they participate in the plugin's decompilation and source-attachment pipeline.
The local run tasks intentionally avoid placing the original vineImplementation jars directly on the Java launch classpath. Instead, each dependency is expanded into a separate directory under the generated Vine runtime-classpath area, and those directories are added to the server classpath.
This prevents a plugin dependency from being represented only as an ordinary JVM jar while still preserving the complete plugin jar in run/mods for manifest discovery. It also prevents the expanded runtime copy's manifest.json from being mistaken for another plugin.
For a single-project run, generated runtime entries are placed under:
build/hytale-vine-runtime/classpath/
For a workspace run, they are placed under:
build/hytale-vine-runtime/workspace-classpath/
These directories are recreated during staging. Do not store manual files in them.
Use manifestDependencies when your mod requires another plugin or module to be present at runtime. The requiredDependency Gradle alias is the matching classpath-oriented convenience configuration, but it does not update this manifest field automatically.
hytaleTools {
manifestDependencies = 'Hytale:AssetModule=*,MoreMagicSpell:Rippod.Hexcode=*,Group:Name=Version'
}Use manifestOptionalDependencies when your mod can integrate with another plugin if it is present,
but does not require that plugin in order to load. The optionalDependency Gradle alias provides compile-time visibility for that integration, but it does not update this manifest field automatically.
hytaleTools {
manifestOptionalDependencies = 'MoreMagicSpell:Rippod.Hexcode=*,Group:Name=Version'
}Manifest dependency values use this format:
Group:Name=Version
Where:
Groupis the dependency group or namespace.Nameis the dependency/plugin/module name.Versionis the required version or version range.*means any compatible version.
Multiple dependencies must be separated with commas and should not include spaces:
hytaleTools {
manifestDependencies = 'GroupOne:DependencyOne=*,GroupTwo:DependencyTwo=1.0.0,GroupThree:DependencyThree=>=2.0.0'
}Do not write spaces after commas:
// Correct
manifestDependencies = 'Hytale:AssetModule=*,MoreMagicSpell:Rippod.Hexcode=*'
// Incorrect
manifestDependencies = 'Hytale:AssetModule=*, MoreMagicSpell:Rippod.Hexcode=*'A dependency should be placed in manifestDependencies if your mod cannot function without it.
A dependency should be placed in manifestOptionalDependencies if your mod only adds support for it when it is available.
For example, if your mod directly requires Hexcode to load:
hytaleTools {
manifestDependencies = 'Hytale:AssetModule=*,MoreMagicSpell:Rippod.Hexcode=*'
}If your mod only enables extra Hexcode integration when Hexcode is installed:
hytaleTools {
manifestOptionalDependencies = 'MoreMagicSpell:Rippod.Hexcode=*'
}The plugin automatically adds the Hytale server dependency based on:
hytaleTools {
hytaleVersion = '0.+'
}This injects:
vineServerJar "com.hypixel.hytale:Server:${hytaleVersion}"and makes it available on compileOnly. You do not need to declare this manually.
hytaleVersion accepts Gradle's dynamic version selectors, which is the easiest way to track the latest server build for a patchline:
hytaleTools {
hytaleVersion = '0.+' // latest 0.x build on the configured patchline
patchline = 'release'
}Supported selectors:
0.+β latest version starting with0.0.5.+β latest version starting with0.5.latest.releaseβ same as+
By default, the generated manifest.json contains the resolved concrete version (e.g. 0.5.0-pre.9 or 2026.1.22-6f8bdbdc4), not the dynamic selector. For example, hytaleVersion = '0.+' resolves the server dependency normally, and the manifest ServerVersion is written as the resolved full version.
If you want the manifest to use a semver range instead, set manifestServerVersion explicitly:
hytaleTools {
hytaleVersion = '0.+'
// Written directly to manifest.json as ServerVersion
manifestServerVersion = '>=0.5.0-pre.9 <0.6.0'
}When manifestServerVersion is set, it only affects the manifest ServerVersion field. hytaleVersion is still used to resolve the Hytale server dependency for development, compilation, and local runs.
hytaleVersion controls which Hytale server artifact Gradle resolves for compilation, IDE setup, and local server runs.
manifestServerVersion controls the ServerVersion field written to manifest.json.
If manifestServerVersion is unset, the plugin resolves the configured Hytale version selector, such as 0.+ or 2026.+, to the full concrete server version and writes it to the manifest as a minimum supported version range.
hytaleTools {
hytaleVersion = '0.+'
}This writes a concrete resolved version, for example:
{
"ServerVersion": ">=0.5.0-pre.9"
}Set manifestServerVersion when you want the manifest itself to accept a semver range:
hytaleTools {
hytaleVersion = '0.+'
manifestServerVersion = '>=0.5.0-pre.9 <0.6.0'
}This writes:
{
"ServerVersion": ">=0.5.0-pre.9 <0.6.0"
}In this setup, hytaleVersion still resolves the development server dependency, while manifestServerVersion is written directly to the manifest.
When using a dynamic selector, the plugin scopes resolution to the configured patchline:
patchline = 'release'resolves only against the Hytale Server Release repopatchline = 'pre-release'resolves only against the Hytale Server Pre-Release repo
This prevents 0.+ from accidentally selecting a pre-release build when you're targeting release, or vice versa. Static versions are unaffected and continue to resolve from whichever repo serves them.
Gradle caches dynamic version resolutions. The plugin configures the vineServerJar configuration with a 10-minute cache window, so a freshly published server build is picked up within that interval. To force an immediate refetch:
./gradlew runServer --refresh-dependenciesThe Hytale server dependency is also marked as changing = true, so the same flag will refetch the artifact bytes if Hytale ever republishes a coordinate.
If you want to use a different version of the Hytale server:
dependencies {
vineServerJar 'com.example:custom-server:1.0.0'
}Auto-injection is skipped when a dependency is already declared.
The plugin automatically adds the AssetBridge library dependency:
dependencies {
implementation 'com.azuredoom.hytale:hytale-asset-editor-runtime:0.2.0'
}This dependency is:
- added automatically via the
hytaleBundledRuntimeconfiguration - available on
implementation
By default, the runtime is bundled into your mod jar (similar to a lightweight shading step without requiring an external plugin).
You can disable this behavior:
hytaleTools {
bundleAssetEditorRuntime = false
}When disabled:
- the dependency is still available at
compile/runtime - it is not included inside the final
jar
You can override the default version:
dependencies {
hytaleBundledRuntime 'com.azuredoom.hytale:hytale-asset-editor-runtime:0.x.0'
}Declaring a dependency manually will replace the pluginβs default.
Example extension usage:
hytaleTools {
javaVersion = project.java_version as Integer
hytaleVersion = project.hytale_version.toString()
patchline = project.hytale_patchline.toString()
// Optional manifest ServerVersion override.
// If unset, dynamic hytaleVersion selectors resolve to the full version in manifest.json.
// manifestServerVersion = '>=0.5.0-pre.9 <0.6.0'
// Optional override to an existing local Assets.zip if you don't want to download/cache it.
// Accepts a direct Assets.zip path, a directory containing Assets.zip,
// or a Hytale launcher/install directory.
// Remember to update this path if changing patchlines/versions.
// hytaleHomeOverride = "/path/to/Hytale/install/release/package/game/latest/Assets.zip"
// Optional override to not generate an Assets Binary (won't show sources as binary in your IDE)
// generateAssetsBinary = true
manifestGroup = project.manifest_group.toString()
modId = project.mod_id.toString()
modDescription = project.mod_description.toString()
modUrl = project.mod_url.toString()
mainClass = project.main_class.toString()
// Author format: Name, or Name|Email, or Name|Email|Url.
// Separate multiple authors with commas. Email and Url are optional.
modCredits = project.mod_credits.toString()
manifestDependencies = project.manifest_dependencies.toString()
manifestOptionalDependencies = project.manifest_opt_dependencies.toString()
curseforgeId = project.curseforgeID.toString()
disabledByDefault = project.disabled_by_default.toString().toBoolean()
includesPack = project.includes_pack.toString().toBoolean()
// Optional: declare sub-plugins (positional style)
subPlugin('Forestry', 'com.example.mods.forestry.ForestryPlugin')
// subPlugin(name, main, disabledByDefault, includesAssetPack, serverVersion)
// Or use the map style for full manifest field control:
// subPlugin([ Name: 'Forestry', Main: '...', Description: 'Adds forestry', DisabledByDefault: false ])
// Optional: declare load ordering
// loadBefore 'otherpluginname'
// loadBefore 'anotherplugin', '>=2.0.0'
serverArgs = ['--allow-op', '--disable-sentry']
serverJvmArgs = ['-Xms1G', '-Xmx2G']
preRunTask = 'generateDevResources'
debugEnabled = false
debugPort = 5005
debugSuspend = false
hotSwapEnabled = false
requireDcevm = false
useHotswapAgent = true
// Optional external HotswapAgent jar
hotswapAgentPath = ''
// Optional JetBrains Runtime location
// jbrHome = '/path/to/jbr'
// Optional: defaults to the release/prerelease docs URL based on patchline
// serverJavadocsUrl = 'https://release.server.docs.hytale.com/'
// Optional: enabled by default
injectServerJavadocsIntoSources = true
}The plugin also reads these Gradle properties automatically:
java_versionhytale_versionmanifest_server_versionhytale_patchlinehytools.hytale.oauth.basehytools.hytale.accounts.basemanifest_groupmod_idmod_descriptionmod_urlmain_classmod_creditsmanifest_dependenciesmanifest_opt_dependenciescurseforgeIDdisabled_by_defaultincludes_packhytools.debug.porthytools.debug.suspendhytools.jbr.homehytools.hotswap.agent.path
The plugin also recognizes these system properties for dev runtime features:
debugβ enables JDWP debug modehotswapβ enables hot swap runtime setup
If you encounter a bug, unexpected behavior, or have a feature request:
- π Open an issue: https://github.com/AzureDoom/Hytale-Gradle-Plugin/issues
- π¬ Join the Discord: https://discord.gg/f2NJGA8ey8
Issue templates are provided for bug reports and feature requests.
Please include:
- Gradle version
- Plugin version
- Full stacktrace (
--stacktrace) - Relevant build configuration
For general questions or help getting started, Discord is usually the fastest way to get support.
Start here first:
./gradlew hytaleDoctorhytaleDoctor prints a summary of:
- configured
hytaleVersionandpatchline - manifest path and run directory
- resolved asset wrapper path and effective
Assets.zippath (local override path whenhytaleHomeOverrideis set, otherwise the Gradle cache path - auth token cache path
- resolved
vineServerJarfiles - declared
vineImplementation,vineCompileOnly,requiredDependency,optionalDependency, andvineDecompileTargetsdependencies
Use it when:
- runServer fails
- assets are missing
- manifest values look wrong
- expected dependency sources are not showing up
requiredDependency and optionalDependency configure Gradle classpaths only. They cannot derive the Hytale manifest group, plugin name, or accepted version range from arbitrary Maven coordinates.
Declare both sides explicitly:
dependencies {
requiredDependency 'com.example:economy-plugin:1.2.0'
optionalDependency 'com.example:placeholder-plugin:2.0.0'
}
hytaleTools {
manifestDependencies = 'Example:Economy=*'
manifestOptionalDependencies = 'Example:Placeholder=*'
}Check both the Gradle configuration and the plugin manifest:
- Use
vineImplementationwhen your code references the dependency's classes and the dependency must run locally. - Use
vineModonly when the plugin should be installed intorun/modswithout shared-classpath access. - Ensure the dependency jar contains
manifest.jsonat the root. Without it,vineImplementationtreats the jar as a normal library and does not copy it intorun/mods. - Declare the runtime dependency in your own
manifestDependencieswhen Hytale must load it before your plugin. - Run
./gradlew prepareRunServer --infoor./gradlew stageAllModAssets --infoand look for whether the dependency was logged as a plugin or a library. - Remove stale manual copies from
run/mods; plugin-managed staged entries are cleaned automatically, but unrelated files are intentionally preserved.
A compile dependency alone does not establish Hytale plugin load ordering. The Gradle dependency and the manifest dependency serve different purposes.
If a build fails with a connection error or timeout pointing at one of the optional mavens (CurseMaven, AzureDoom Maven, PlaceholderAPI, Hytale Modding Maven, Hytale-Mods.info Maven), the Modtale resolver, or Modifold, that host is likely temporarily down. Disable it while it recovers:
hytaleTools {
disableCurseMaven = true
}Or globally via gradle.properties, without touching individual mod build scripts:
hytools.repos.disableCurseMaven=trueSee Repositories for the full list of toggles. Note that disabling a repository does not remove any dependencies you've declared against it β if the build then fails to resolve, that dependency simply cannot resolve until the repository is re-enabled or the outage passes.
If hytaleVersion is not set and no vineServerJar is declared,
tasks that require the server jar may fail.
Always configure:
hytaleTools {
hytaleVersion = '...'
}If you're using a dynamic selector like 0.+ and Gradle is picking the wrong version (or claims it can't find any matching version), check the following:
- Use
+, not*. Gradle's dynamic version syntax is0.+, not0.*. The*form is treated as a literal version string and will fail to resolve. - Confirm
patchlineis set correctly. Dynamic resolution is scoped to the active patchline. If you're tracking pre-release builds, setpatchline = 'pre-release'(orhytale_patchline=pre-releaseingradle.properties). - Force a refresh. Dynamic versions are cached for 10 minutes. Run
./gradlew --refresh-dependenciesto bypass the cache and refetch the latest version listing. - Check
hytaleDoctor. The diagnostic task prints the configured patchline and the resolved server jar files so you can confirm what was actually picked.
Run:
./gradlew prepareDecompiledSourcesForIdeThis allows IDEs to attach readable generated source code instead of showing only compiled classes.
Then refresh or reimport the Gradle project in your IDE.
Also verify the dependency is listed in vineDecompileTargets if you expect decompiled dependency sources.
The plugin injects hosted Hytale API docs into generated server sources during prepareDecompiledSourcesForIde. If source comments are missing:
- Confirm
injectServerJavadocsIntoSourcesis not disabled. - Confirm
serverJavadocsUrlpoints at the correct release or pre-release docs site. - Delete the generated source jars and Javadoc cache, then regenerate sources.
rm -rf build/vineflower/hytale-server
rm -rf build/generated-sources-jars/server
rm -rf build/generated-sources-m2
rm -rf build/generated-sources-ivy
rm -rf ~/.gradle/caches/hytale-javadocs/release
./gradlew prepareDecompiledSourcesForIde --rerun-tasksThe injector only adds comments where a matching hosted Javadoc page and member documentation can be found.
To force a full regeneration including the global caches:
rm -rf build/vineflower
rm -rf build/generated-sources-jars
rm -rf build/generated-sources-m2
rm -rf build/generated-sources-ivy
rm -rf ~/.gradle/caches/hytale-decompiled
rm -rf ~/.gradle/caches/hytale-javadocs/release
./gradlew prepareDecompiledSourcesForIde --rerun-tasksOnly clear ~/.gradle/caches/hytale-decompiled if you want to force a full re-decompile across all projects. Deleting it will also affect other projects on the same machine until they regenerate the cache.
Run:
./gradlew downloadAssetsZipAlso verify:
hytale_versionis set correctlyhytale_patchlinematches the artifact you expect- authentication cache under Gradle user home is valid
Run:
./gradlew updatePluginManifestThe build also wires manifest validation automatically, so this usually indicates missing configuration values.
Verify the configured values for:
manifestGroupmodIdmainClasshytaleVersion
Also review:
manifest_dependenciesmanifest_opt_dependenciesincludes_packAuthors, if present, must be an array of objects with a non-blankName; optionalEmailandUrlvalues must not be blank
Make sure the dependency is declared in:
vineDecompileTargets "group:module:version"Only dependencies in that configuration are decompiled for IDE attachment.
Verify that:
- the task name matches exactly
- the task is registered in the same project as
runServer preRunTaskis set to the task name as a string
Run:
./gradlew hytaleJvmDoctorUse it to verify:
- which Java executable
runServerwill use - whether JetBrains Runtime was detected
- whether enhanced class redefinition is supported
- whether bundled HotswapAgent support is available
If hot swap is not working as expected, this should be the first check.
If hotswapAgentPath or hytools.hotswap.agent.path is set, the file must exist and point directly to a HotswapAgent jar.
Example:
hytaleTools {
hotSwapEnabled = true
useHotswapAgent = true
hotswapAgentPath = '/absolute/path/to/hotswap-agent.jar'
}Or:
hytools.hotswap.agent.path=/absolute/path/to/hotswap-agent.jarIf the file does not exist, runServer fails early with a clear error instead of launching without the agent.
If you launch runServer from IntelliJ using Debug, IntelliJ may inject its own JDWP debugger agent.
The plugin checks for an existing JDWP argument before adding its own debug agent. This prevents JVM startup errors such as:
Cannot load this JVM TI agent twice, check your java command line for duplicate jdwp options.
For command-line debugging, use:
./gradlew runServer -Ddebug=trueFor IntelliJ debugging, using the IDE's Debug action is usually enough. Avoid manually adding another -agentlib:jdwp=... entry in serverJvmArgs unless you know IntelliJ is not already providing one.
- Requires Java 25+
releaseis the default patchline- The plugin applies the
javaplugin automatically - The plugin does not apply the
ideaplugin automatically - If the
ideaplugin is present, IDEA integration is wired automatically - You can always run
prepareDecompiledSourcesForIdedirectly for source generation - Hosted Hytale Javadoc injection is enabled by default for generated server sources and uses a Gradle-user-home cache