Skip to content

Data Patches

Data patches are a powerful tool for modifying properties of blocks, units and items on a per-map or per-server basis. Some applications of this feature include balance patches, custom game modes, or different game progression.

Some things that data patches can do:

  • Make a factory use more or less of a resource
  • Change the resource requirements of a block
  • Completely change the weapons and bullets of a unit
  • Change the recipes available to a unit factory
  • Change displayed sprites of block or units to other in-game textures
  • Change the build speed, health, armor, etc. of any block or unit
  • Change the type of a unit (e.g. ground to flying)

Some of the things that data patches cannot do:

  • Introduce new textures or assets into the game
  • Introduce new mechanics not present in vanilla
  • Change the types of blocks (e.g. wall into turret)
  • Add new content (items, blocks, units, etc)

Balance patches are not a replacement for mods; they can only tweak existing content.

Writing A Trivial Data Patch

Data patches are written in JSON or HJSON (a superset of JSON). You will need a text editor to write them; there is currently no in-game editor for patches.

For the sake of brevity, this guide will only show how to write patches in HJSON.

To begin, create a text file named mypatch.hjson, with the following contents:

//naming a patch is optional, but helps identify it in the UI
name: My Patch

//makes all conveyors have 50 max health
block.conveyor.health: 50

Applying Data Patches

Applying Patches on Maps

Open your map in the map editor, then open the top-left menu (or press ESC on desktop). Go to Map Info -> Data Patches -> Add, and then specify the patch file you saved in the previous step. You can view any errors generated by the patch by clicking the ⚠️ icon, or reload the file with the 🔁 button.

Applying Patches on Dedicated Servers

Open your server directory (which should already contain folders for plugins, maps, saves, etc), locate the patches directory, and simply drop your patch file into it. Patch files must have a hjson/json/json5 extension to be loaded. They will automatically be applied to any map after any other data patch that the map already contains.

If you've done everything correctly, the server should log the amount of patch files loaded upon startup. Any warnings or errors will also be printed to the console. To reload patches while the server is still running, use the reloadpatches command.

Data Patch Basics

Data patches follow a heirarchy. At the first level, you define the type of content to be changed: block, liquid, item, unit, weather, etc.

At the second level, you define the name of the content to be edited, e.g. conveyor, copper, copper-wall-large. These names are case-sensitive, and will be shown under the content's name in the Core Database if you have the console enabled.

At the third level, you define the properties you will be changing, and their corresponding values. As an example:

block: {
  conveyor: {
    health: 50
    //other properties of the conveyor go here...
  }
  //other blocks here...
}
//other types of content here...

Alternatively, if you are only modifying a single property, it may be more convenient to use the shorthand syntax:

block.conveyor.health: 50

Viewing All Fields for Content

If you are familiar with Java, you can view the relevant content source file, e.g. Block.java for blocks.

If not, you can enable the console in game settings, then click the "View Content Fields" button in the database for a specific block, unit or liquid.

Note that this will only display the fields for that specific class - if you want to see the fields for the superclass, click the link to the page immediately after "extends" on that page.

For example, Conveyors will have all the fields show on their page, as well as everything in the Block superclass.

Accessing Arrays/Sequences

When accessing an array (T[]) or sequence (Seq) is necessary, you may want to do it like so:

//greatly offsets one of dagger's two weapons
unit.dagger.weapons.0.x: 100

Note that modifying the bullet of mirrored weapons will affect both sides, as they share the same bullet type:

//this wall make *both* dagger weapons deal 55 damage
unit.dagger.weapons.0.bullet.damage: 55

Adding vs Overwriting to Arrays/Sequences

Sometimes, it may be necessary to add to a sequence, instead of overwriting it, like so:

//adds a ridiculously overpowered laser to flare's weapons, keeping the others intact
//note the .+ before the field assignment; this adds the element
//also note that it can be a single element, not an array!
unit.flare.weapons.+: {
  x: 0
  y: 0
  reload: 10
  bullet: {
    type: LaserBulletType
    damage: 100
  }
}

Alternatively, you can overwrite the array:

//flare will *only* have this weapon now, its old ones will be overwritten
//also note that, when overwriting, you *have* to use the array brackets [], since you are assigning a new value
unit.flare.weapons: [
  {
    x: 0
    y: 0
    reload: 10
    bullet: {
      type: LaserBulletType
      damage: 100
    }
  }
]

This syntax works for fields of type T[], Seq<T> and ObjectSet.

General Information

  • Time values are generally measured in ticks (sometimes called 'frames'), which are 1/60th of a second (60 ticks = 1 second). If a field describes a duration, it is probably in ticks. If a field describes a rate of consumption, it is probably in "units per tick".
  • Distances, positions and sizes are generally measured in 'world units', which are 1/8th of a tile. This is because Mindustry used to have 8x8 sprites, and 1 world unit used to be 1 pixel. This is horrible and unintuitive, but it remains for historical reasons.
  • When you interact with coordinates through a logic processor, they are internally converted to and from world units. Patches modify fields directly, and don't have this luxury.
  • Item and liquid stacks can be defined as 'name/amount'. For example, a ItemStack field can have a value of thorium/100.

Caveats & Limitations

  • Creating or assigning unit weapons with mirror: true will not work, as initialization is not re-run for units. You will have to manually create a mirrored version and assign x/shootX/flipSprite.
  • Unit range and maxRange will not update, even if you increase the lifetime or speed of a unit's bullet. These values have to be assigned manually.
  • Similarly, unit fields won't update if you reassign its type. Reassigned naval units will still drown, for example.
  • clipSize will not update for blocks or units, even if you make them draw larger sprites. Assign this manually.
  • Many consumer types will not work for blocks that were not designed to support them. As a general rule, if it isn't used in vanilla, it probably isn't well supported.
  • Sizes of blocks cannot be reassigned, as that would catastrophically break saves, and is completely non-functional for most transport blocks.
  • Values that depend on other values will not be re-assigned. For example, changing the item build requirements for a block will not change its build time as it would in a mod.
  • Environmental (static) blocks cannot use textures that are outside the environmental atlas page. In other words, you can make grass use the snow sprite, but you cannot make grass use the router sprite; it will display an error texture in-game.
  • Textures will not be reloaded after a patch is applied. For example, if you change the suffix or name of a DrawRegion, it will not do anything, because load will not be called again. Create an entirely new object instead.
  • Reassigning health of units/blocks will not update the current health of any existing units/blocks. If you increase the maximum health of a block, all existing buildings of that type on the map will appear damaged.
  • It is quite possible for patches to crash or freeze the game. Please report this if it happens, but keep in mind that I may not be able to fix all of these issues - keep it reasonable. Bug reports of the form "I made this unit spawn 9 trillion bullets and my game froze" will be ignored.

Extra Examples

'Duoification'

//once again, names are optional, but help identify a patchset in the list
name: Duofication

item: {
  //fissile-matter is an unused item, so use it for demonstration
  fissile-matter: {
    //change the display name of the item to 'Duo'
    localizedName: Duo
    //unhide it
    hidden: false
    //change the in-game icon to 'duo-preview', which is used by the duo turret
    fullIcon: duo-preview
    //change the in-ui icon to 'block-duo-ui', which is also used by the duo turret in UI
    uiIcon: block-duo-ui
  }
}

block: {
  //edit the pulverizer
  pulverizer: {
    //change its name
    localizedName: Duo Factory
    //rewrite the things it consumes
    consumes: {
      //remove all previous consumers - without this line, it would retain its old consumption of scrap
      //you can also remove *only* item consumers by writing `remove: items`
      remove: all
      //consume 1 copper item per craft
      item: copper
    }
    //change the UI display icon
    uiIcon: block-duo-ui
    //change the region
    region: block-duo-full
    //output 1 fissile matter, which was previously patched to have the name 'Duo'
    //note that outputItems is an array, so its contents have to be written as a list with [ and ]
    outputItems: [fissile-matter/1]
    //define the drawers, which define how the block is rendered. they can be defined as an array with []
    drawer: [
      {
        //the first drawer is a simple DrawRegion, which draws the sprite 'block-1', which is the base for 1x1 Serpulo turrets
        type: DrawRegion
        name: block-1
      }
      {
        //the second drawer draws the 'duo-preview' region and rotates it at speed 1
        type: DrawRegion
        rotateSpeed: 1
        name: duo-preview
      }
    ]
  }

}

unit: {
  //patch the dagger unit
  dagger: {
    //change its body region to duo-preview
    region: duo-preview
    //re-define the weapon array (note: this clears all previous weapons)
    weapons: [
      //all weapons in the array are objects of their own, so they need to be encased in {} braces
      {
        //the weapon is centered on the unit
        x: 0
        y: 0
        //reload of 20 ticks (1 second = 60 ticks)
        reload: 20
        //alternate left and right with a spread of 3.5 world units (1 tile = 8 world units)
        shoot: {
          type: ShootAlternate
          spread: 3.5
        }

        //define the bullet the weapon shoots
        bullet: {
          //width and height of the sprite in world units
          width: 7
          height: 9
          //lifetime in ticks (1 second)
          lifetime: 60
          //colors of the bullet as a hex code
          frontColor: eac1a8
          backColor: d39169
        }
      }
    ]
  }
}

Modifying Turret Ammo

block.fuse.ammoTypes: {
  //remove titanium ammo from the ammo map by using the special "-" value
  titanium: "-"
  //add surge alloy ammo that shoots a laser
  surge-alloy: {
    type: LaserBulletType
    //make it produce 1 shot per ammo item
    ammoMultiplier: 1
    //make it shoot half as fast
    reloadMultiplier: 0.5
    damage: 100
    //make it look awful!
    colors: ["000000", "ff0000", "ffffff"]
  }
}

Adding Unit Abilities

//add a new ability to pulsar (note the .+)
unit.pulsar.abilities.+: [
  {
    type: ForceFieldAbility
    //set the maximum health of of the force field to 1000
    max: 1000
  }
]

Adding Unit Plans

//make the ground factory produce flares
block.ground-factory.plans.+: {
  unit: flare
  //require 10 surge alloy to build
  requirements: [surge-alloy/10]
  //take 100 ticks to build
  time: 100
}

Modifying Unit Factory Plans

//make daggers (the first plan of the ground factory, or index 0) take 60 ticks to build, or 1 second 
block.ground-factory.plans.0.time: 60

Modifying Block Requirements

//make duo cost 5 titanium and 20 surge alloy
block.duo.requirements: [titanium/5, surge-alloy/20]