promotional bannermobile promotional banner

Patchly

The Perfect Pure Patch Plugin. A modern in-place Zima, Patchwork, and Hytalor Alternative

Patchly - The Pure Patcher Plugin

For mod developers, use com.riprod:patchly:3.+ An in-place modern Zima and Hytalor Alternative

While Patchly is compatible with Hytalor and Zima - two other patching solutions, it is not recommended to have all three installed. There are no guarantees on reliability in this case. Patchly has 100% of the functionality of the other two, so migrating to just one patching solution is preferred

Patch JSON assets without rewriting it from the ground up. Zero dependencies.

Why Patchly Exists

Hytale's native Parent: super only inherits at the outer asset level - most nested codec fields (e.g. Item.armor.StatModifiers, DamageResistance) use .append(...) not .appendInherited(...), so a normal JSON override replaces the whole sub-object and silently wipes everything it didn't restate.

What patchly does

Patchly reads the resolved base asset and deep-merges your .patch onto it, so you only write the diff.

Works on every registered AssetPack - folder, .zip, and .jar packs all. JSON-only modders can drop Patchly.jar into mods/; Java modders can bundle Patchly into their own jar via Gradle Shadow. Patchly coordinates with other instances of patchly to ensure only one patchly is running at a time. This means that your mod can work by itself, with other patchly mods, with the patchly.jar, and more.

How patches work

Place a .patch file at the same path as the asset you're patching, swapping .json for .patch. To patch Armor_Iron_Head.json in another pack, ship Server/Item/Items/Armor/Iron/Armor_Iron_Head.patch in your pack.

Patchly walks every pack, resolves the latest version of each target, merges your .patch onto it, and writes the result into a synthetic override pack that takes precedence.

Minimal patch

{
  "Armor": {
    "StatModifiers": {
      "Mana": [{ "Amount": 126, "CalculationType": "Additive" }]
    }
  }
}

Deep-merges field-by-field. Mana lands inside the parent's existing StatModifiers block; Health and other siblings stay intact.

[] Complex Array Operations

+ Replace vs append on arrays

Arrays REPLACE by default. Suffix the key with `+` to append instead:
{
  "BlockType": {
    "Bench": {
      "Categories+": [
        { "Id": "Arcane_Hexcode", "Icon": "...", "Name": "..." }
      ]
    }
  }
}
The parent's existing `Categories` entries stay; this entry gets added to the end.

- Prepend on arrays

Suffix the key with `-` to add your entries at the FRONT instead of the end:
{
  "Children-": [
    { "Id": "Hexcode", "Name": "hexcode.itemcategory.hexcode.name" }
  ]
}
Your entries land before the parent's, in the order you wrote them. `-` is symmetric with `+` (creates the array if absent, supports `$Match`). It means prepend, NOT remove.

`?` Fill only if a key is missing

Suffix the key with `?` to write a value only when the target does not already define it. If the key is present, the base wins and your value is dropped:
{
  "Armor": {
    "StatModifiers": {
      "Mana?": [{ "Amount": 200, "CalculationType": "Additive" }]
    }
  }
}
An item that already has `Mana` keeps it; one without gets `200`. Presence-based, so it works for any value type, and it is decided per key. This is the only way to let the base win - `$Priority` only orders patches against each other, never against the base.

$Requires - Feature Flag or Dependent Pack Gates

Single pack:
{
  "$Requires": "Riprod:Hexcode",
  "Armor": { ... }
}
Multiple packs (all must be present):
{
  "$Requires": ["Riprod:Hexcode", "Author:SomeOtherPack:^0.5.0"],
  "Armor": { ... }
}
Pack ids are matched against `AssetPack.getName()` (i.e. `Group:Name` from the target mod's manifest). If anything's missing the patch is skipped with a log line. There is currently no support for excluding packs. This was deemed unnecessary, open a PR if you wish to have this functionality.

$Priority - Specify a Winner on Conflicts

Integer, default `0`. Lower applies first, higher applies last → higher wins on field conflicts. Tie-break is pack load order.
{
  "$Priority": 100,
  "Armor": {
    "StatModifiers": {
      "Mana": [{ "Amount": 9999, "CalculationType": "Additive" }]
    }
  }
}
Two mods patching the same field both apply, but the higher `$Priority` writes last. Lower-priority `+` appends still stack onto fields the higher patch didn't touch.

Global.vars - Reusable centralized variables

Anywhere in your pack, you can define variables
// ArmorVariables.vars
{
   "AddArmor": true,
   "ArmorLevel": 15
}
And then reference those variables in your pack, with math!
{
   "$Requires": "$ArmorVariables.AddArmor", // only applies if armor is enabled
   "ItemLevel#": "$ArmorVariables.ArmorLevel * 3" // level = 45 
}

Integrating with Patchly

You can implement patchly in two ways...

As a standalone (asset-packs)

Drop `Patchly-X.Y.Z.jar` into your server's `mods/` folder alongside your asset pack. That's it. Patchly will scan, merge, register. You can also add it as a required dependency in your package.json as well as adding it as a relation in curseforge for better visibility!

As a bundled dep (Java mods)

Add the Shadow plugin and depend on the lib jar:
plugins {
    id 'java'
    // Recommended to use azuredoom.hytale-tools as the dev wrapper. Otherwise, include https://maven.hytalemodding.dev/releases
    id 'com.azuredoom.hytale-tools' version '1.0.48'
    id 'com.gradleup.shadow' version '8.3.5'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of((project.java_version as String).toInteger())
    }
}

// a dedicated configuration so ONLY Patchly is shaded, not your compile deps
configurations {
    shaded
}

dependencies {
    // compile against the API, and mark it for shading into the final jar
    // 3.+ tracks the latest 3.x release without jumping a breaking major
    shaded 'com.riprod:patchly:3.+'
    implementation 'com.riprod:patchly:3.+'
}

tasks.named('shadowJar') {
    archiveClassifier = ''                              // shadow jar IS the published artifact
    mergeServiceFiles()
    configurations = [project.configurations.shaded]    // shade only what's in `shaded`
}

tasks.named('jar') { enabled = false }                  // disable the thin jar
tasks.named('build') { dependsOn tasks.named('shadowJar') }
Then in your `JavaPlugin`:
import com.riprod.patchly.PatchManager;

public final class MyPlugin extends JavaPlugin {
    private final PatchManager patchManager;

    public MyPlugin(JavaPluginInit init) {
        super(init);
        patchManager = new PatchManager(this);
    }

    @Override protected void setup() {
        patchManager.install();
    }
}
> Note: When doing `./gradlew runServer` for testing, you cannot have Patchly.jar in the ./mods folder as well as your dependency. This is because the devserver will scan your deps and find your Patchly.jar's manifest.json and register it also as it's own pack. if it also exists in ./mods/ then it will register twice and die. You do not need Patchly.jar in your mods/ folder anyways, so just delete it lol
If both standalone `Patchly.jar` and a bundling mod are installed in the same JVM, each votes its version and the newest one becomes the single active owner; older copies defer and noop. The decision is final at boot. No duplicate work, no conflicts.

Additional Features

  • Hot Reloading
  • Multi-pack handling
  • Bulk Patches / Replacements
  • Feature Flags
  • Variables / Derived Values
References
Devly - Patchly Developer Toolings
Enables developers to convert json files into .patch files and live-reload edits
Patchly Wiki Documentation
Goes in-depth into every available option inside of Patchly - the path forwards if you want sophisticated patches, variables, or feature flags

The Patchly Team

profile avatar
  • 16
    Followers
  • 13
    Projects
  • 21.0K
    Downloads
Donate

More from ItsRiprodView all

  • Effectly project image

    Effectly

    The Universal Ability API Done Right

    • 64
    • September 4, 2026
  • Hexcode - Magic by Design project image

    Hexcode - Magic by Design

    Magic is unexplained science. To make magic, you must first invent science.

    • 8.9K
    • August 29, 2026
  • More Magic Spells project image

    More Magic Spells

    This mod adds powerful magic spells to Hytale, allowing for example for players to petrify enemies, reshape the battlefield with visually striking, temporary effects, with much more to come soon!

    • 1.6K
    • August 27, 2026
  • Waypointly project image

    Waypointly

    Allows players to manage (create, edit, and teleport) waypoints with a clean UI, and display them in your map and in the compass

    • 65
    • August 27, 2026
  • Effectly project image

    Effectly

    The Universal Ability API Done Right

    • 64
    • September 4, 2026
  • Hexcode - Magic by Design project image

    Hexcode - Magic by Design

    Magic is unexplained science. To make magic, you must first invent science.

    • 8.9K
    • August 29, 2026
  • More Magic Spells project image

    More Magic Spells

    This mod adds powerful magic spells to Hytale, allowing for example for players to petrify enemies, reshape the battlefield with visually striking, temporary effects, with much more to come soon!

    • 1.6K
    • August 27, 2026
  • Waypointly project image

    Waypointly

    Allows players to manage (create, edit, and teleport) waypoints with a clean UI, and display them in your map and in the compass

    • 65
    • August 27, 2026