KubeJS

This wiki is still very much work in progress! Feel free to suggest changes in #kubejs-and-code Discord channel.

Information (Read this first!)

Website and FAQ: https://kubejs.latvian.dev/

Discord server: https://discord.gg/bPFfH6P

Source and issue tracker: https://github.com/KubeJS-Mods/KubeJS


Forge download: https://www.curseforge.com/minecraft/mc-mods/kubejs-forge


Fabric download: https://www.curseforge.com/minecraft/mc-mods/kubejs-fabric


1.12 versions aren't supported, only 1.15 and up!


How to read Variables and Functions
Format Use Example Info
variableName

var x = event.variableName

event.variableName = 10

A simple variable, can be read and changed as well
functionName() event.functionName() Function with no parameters, can be called
functionName(int x, String y)

event.functionName(10, 'abc')

Function with parameters with types specified (even if JS doesn't have function types), can be called
variableName var x = event.variableName Variable that can only be read but can't be changed
variableName event.variableName = 10 Variable that can only be changed, but can't be read

Events

Events that get fired during game to control recipes, world, etc.

Events

List of all events

This is a list of all events. It's possible that not all events are listed here, but this list will be updated regularly.

Click on event ID to open it's class and see information, fields and methods.

Type descriptions:

ID Cancellable Type
init No Startup
postinit No Startup
loaded No Startup
command.registry No Server
command.run Yes Server
client.init No Client
client.debug_info.left No Client
client.debug_info.right No Client
client.logged_in No Client
client.logged_out No Client
client.tick No Client
server.load No Server
server.unload No Server
server.tick No Server
server.datapack.first No Server
server.datapack.last No Server
recipes No Server
world.load No Server
world.unload No Server
world.tick No Server
world.explosion.pre Yes Server
world.explosion.post No Server
player.logged_in No Server
player.logged_out No Server
player.tick No Server
player.data_from_server. Yes Client
player.data_from_client. Yes Server
player.chat Yes Server
player.advancement No Server
player.inventory.opened No Server
player.inventory.closed No Server
player.inventory.changed No Server
player.chest.opened No Server
player.chest.closed No Server
entity.death Yes Server
entity.attack Yes Server
entity.drops Yes Server
entity.check_spawn Yes Server
entity.spawned Yes Server
block.registry No Startup
block.missing_mappings No Server
block.tags No Server
block.right_click Yes Server
block.left_click Yes Server
block.place Yes Server
block.break Yes Server
block.drops No Server
item.registry No Startup
item.missing_mappings No Server
item.tags No Server
item.right_click Yes Server
item.right_click_empty No Server
item.left_click No Server
item.entity_interact Yes Server
item.modification No Startup
item.pickup Yes Server
item.tooltip No Client
item.toss Yes Server
item.crafted No Server
item.smelted No Server
fluid.registry No Startup
fluid.tags No Server
entity_type.tags No Server
worldgen.add No Startup
worldgen.remove No Startup

 

Events

EventJS

This event is the most basic event class, parent of all other events.

Parent class

Object

Can be cancelled

No

Variables and Functions

Name Return Type Info
cancel() void Cancels event. If the event can't be cancelled, it won't do anything.
Events

CommandEventJS

This event needs cleanup! Using it is not recommended.

Information

This event is fired when a command is executed on server side.

Parent class

EventJS

Can be cancelled

Yes

Variables and Functions

Name Type Info
parseResults ParseResults<CommandSource> Command params 
exception Exception Error, set if something went wrong



Events

TagEventJS

This event is fired when a tag collection is loaded, to modify it with script. You can add and remove tags for items, blocks, fluids and entity types.

Parent class

EventJS

Can be cancelled

No

Variables and Functions

Name Type Info
type String Tag collection type.
get(String tag) TagWrapper Returns specific tag container which you can use to add or remove objects to. tag parameter can be something like 'forge:ingots/copper'. If tag doesn't exist, it will create a new one.
add(String tag, String[]/Regex ids) TagWrapper Shortcut method for event.get(tag).add(ids).
remove(String tag, String[]/Regex ids) TagWrapper Shortcut method for event.get(tag).remove(ids).
removeAll(String tag) TagWrapper Shortcut method for event.get(tag).removeAll().
removeAllTagsFrom(String[] ids) void Removes all tags from object

TagWrapper class

Variables and Functions

Name Type Info
add(String[]/Regex ids) TagWrapper (itself) Adds an object to this tag. If string starts with # then it will add all objects from the second tag. It can be either single string, regex (/regex/flags) or array of either.
remove(String[]/Regex ids) TagWrapper (itself) Removes an object from tag, works the same as add().
removeAll() TagWrapper (itself) Removes all entries from tag.

Examples

// Listen to item tag event
onEvent('item.tags', event => {
  // Get the #forge:cobblestone tag collection and add Diamond Ore to it
  event.add('forge:cobblestone', 'minecraft:diamond_ore')
  
  // Get the #forge:cobblestone tag collection and remove Mossy Cobblestone from it
  event.remove('forge:cobblestone', 'minecraft:mossy_cobblestone')
  
  // Get #forge:ingots/copper tag and remove all entries from it
  event.removeAll('forge:ingots/copper')
  
  // Required for FTB Quests to check item NBT
  event.add('itemfilters:check_nbt', 'some_item:that_has_nbt_types')
  
  // You can create new tags the same way you add to existing, just give it a name
  event.add('forge:completely_new_tag', 'minecraft:clay_ball')
})

Recipes use item tags, not block or fluid tags, even if items representing those are blocks. Like minecraft:cobblestone even if it's a block, it will still be an item tag for recipes.

block.tags event is for adding tags to block types, it works the same way. You can find existing block tags if you look at a block with F3 mode enabled, on side. These are mostly only used for technical reasons, and like mentioned above, if its for recipes/inventory, you will want to use item.tags even for blocks.

Events

RecipeEventJS

Examples

The most basic script to add a single recipe:

onEvent('recipes', event => {
  event.shaped('3x minecraft:stone', [
    'SAS',
    'S S',
    'SAS'
  ], {
    S: 'minecraft:sponge',
    A: 'minecraft:apple'
  })
})

The most basic script to remove a recipe:

onEvent('recipes', event => {
  event.remove({output: 'minecraft:stick'})
})

Example recipe script:

// kubejs/server_scripts/example.js
// This is just an example script to show off multiple types of recipes and removal methods
// Supports /reload

// Listen to server recipe event
onEvent('recipes', event => {
  // Remove broken recipes from vanilla and other mods
  // This is on by default, so you don't need this line
  //event.removeBrokenRecipes = true

  event.remove({}) // Removes all recipes (nuke option, usually not recommended)
  event.remove({output: 'minecraft:stone_pickaxe'}) // Removes all recipes where output is stone pickaxe
  event.remove({output: '#minecraft:wool'}) // Removes all recipes where output is Wool tag
  event.remove({input: '#forge:dusts/redstone'}) // Removes all recipes where input is Redstone Dust tag
  event.remove({mod: 'quartzchests'}) // Remove all recipes from Quartz Chests mod
  event.remove({type: 'minecraft:campfire_cooking'}) // Remove all campfire cooking recipes
  event.remove({id: 'minecraft:glowstone'}) // Removes recipe by ID. in this case, data/minecraft/recipes/glowstone.json
  event.remove({output: 'minecraft:cooked_chicken', type: 'minecraft:campfire_cooking'}) // You can combine filters, to create ANDk logic
  
  // You can use 'mod:id' syntax for 1 sized items. For 2+ you need to use '2x mod:id' or Item.of('mod:id', count) syntax. If you want NBT or chance, 2nd is required

  // Add shaped recipe for 3 Stone from 8 Sponge in chest shape
  // (Shortcut for event.recipes.minecraft.crafting_shaped)
  // If you want to use Extended Crafting, replace event.shapeless with event.recipes.extendedcrafting.shapeless_table
  event.shaped('3x minecraft:stone', [
    'SAS',
    'S S',
    'SAS'
  ], {
    S: 'minecraft:sponge',
    A: 'minecraft:apple'
  })

  // Add shapeless recipe for 4 Cobblestone from 1 Stone and 1 Glowstone
  // (Shortcut for event.recipes.minecraft.crafting_shapeless)
  // If you want to use Extended Crafting, replace event.shapeless with event.recipes.extendedcrafting.shaped_table
  event.shapeless('4x minecraft:cobblestone', ['minecraft:stone', '#forge:dusts/glowstone'])

  // Add Stonecutter recipe for Golden Apple to 4 Apples
  event.stonecutting('4x minecraft:apple', 'minecraft:golden_apple')
  // Add Stonecutter recipe for Golden Apple to 2 Carrots
  event.stonecutting('2x minecraft:carrot', 'minecraft:golden_apple')

  // Add Furnace recipe for Golden Apple to 3 Carrots
  // (Shortcut for event.recipes.minecraft.smelting)
  event.smelting('2x minecraft:carrot', 'minecraft:golden_apple')
  // Similar recipe to above but this time it has a custom, static ID - normally IDs are auto-generated and will change. Useful for Patchouli
  event.smelting('minecraft:golden_apple', 'minecraft:carrot').id('mymodpack:my_recipe_id')

  // Add similar recipes for Blast Furnace, Smoker and Campfire
  event.blasting('3x minecraft:apple', 'minecraft:golden_apple')
  event.smoking('5x minecraft:apple', 'minecraft:golden_apple')
  event.campfireCooking('8x minecraft:apple', 'minecraft:golden_apple')
  // You can also add .xp(1.0) at end of any smelting recipe to change given XP
  
  // Add a smithing recipe that combines 2 items into one (in this case apple and gold ingot into golden apple)
  event.smithing('minecraft:golden_apple', 'minecraft:apple', 'minecraft:gold_ingot')

  // Create a function and use that to make things shorter. You can combine multiple actions
  let multiSmelt = (output, input, includeBlasting) => {
    event.smelting(output, input)
    
    if (includeBlasting) {
      event.blasting(output, input)
    }
  }
  
  multiSmelt('minecraft:blue_dye', '#forge:gems/lapis', true)
  multiSmelt('minecraft:black_dye', 'minecraft:ink_sac', true)
  multiSmelt('minecraft:white_dye', 'minecraft:bone_meal', false)

  // If you use custom({json}) it will be using vanilla Json/datapack syntax. Must include "type": "mod:recipe_id"!
  // You can add recipe to any recipe handler that uses vanilla recipe system or isn't supported by KubeJS
  // You can copy-paste the json directly, but you can also make more javascript-y by removing quotation marks from keys
  // You can replace {item: 'x', count: 4} in result fields with Item.of('x', 4).toResultJson()
  // You can replace {item: 'x'} / {tag: 'x'} with Ingredient.of('x').toJson() or Ingredient.of('#x').toJson()
  // In this case, add Create's crushing recipe, Oak Sapling to Apple + 50% Carrot
  
  // Important! Create has integration already, so you don't need to use this. This is just an example for datapack recipes!
  event.custom({
    type: 'create:crushing',
    ingredients: [
      Ingredient.of('minecraft:oak_sapling').toJson()
    ],
    results: [
      Item.of('minecraft:apple').toResultJson(),
      Item.of('minecraft:carrot').withChance(0.5).toResultJson()
    ],
    processingTime: 100
  })
  
  // Example of using items with NBT in a recipe
  event.shaped('minecraft:book', [
    'CCC',
    'WGL',
    'CCC'
  ], {
    C: '#forge:cobblestone',
    // Item.of('id', {key: value}), it's recommended to use /kubejs hand
    L: Item.of('minecraft:enchanted_book', {StoredEnchantments:[{lvl:1,id:"minecraft:sweeping"}]}),
    // Same principle, but if its an enchantment, there's a helper method
    W: Item.of('minecraft:enchanted_book').enchant('minecraft:respiration', 2),
    G: '#forge:glass'
  })
  
  // In all shapeless crafting recipes, replace any planks with Gold Nugget in input items
  event.replaceInput({type: 'minecraft:crafting_shapeless'}, '#minecraft:planks', 'minecraft:gold_nugget')
  
  // In all recipes, replace Stick with Oak Sapling in output items 
  event.replaceOutput({}, 'minecraft:stick', 'minecraft:oak_sapling')
})

Possible settings you can change for recipes. It's recommended that you put this in it's own server scripts file, like settings.js

// priority: 5

// Enable recipe logging, off by default
settings.logAddedRecipes = true
settings.logRemovedRecipes = true
// Enable skipped recipe logging, off by default
settings.logSkippedRecipes = true
// Enable erroring recipe logging, on by default, recommended to be kept to true
settings.logErroringRecipes = false

As mentioned before, you can add any recipe from any mod with JSON syntax (see event.custom({})) but these mods are supported as addons with special syntax:

Events

WorldgenAddEventJS

This event isn't complete yet and can only do basic things. Adding dimension-specific features also isn't possible yet, but is planned.

Example script: (kubejs/startup_scripts/worldgen.js)

onEvent('worldgen.add', event => {
  event.addLake(lake => { // Create new lake feature
    lake.block = 'minecraft:diamond_block' // Block ID (Use [] syntax for properties)
    lake.chance = 3 // Spawns every ~3 chunks
  })

  event.addOre(ore => {
    ore.block = 'minecraft:glowstone' // Block ID (Use [] syntax for properties)
    ore.spawnsIn.blacklist = false // Inverts spawn whitelist
    ore.spawnsIn.values = [ // List of valid block IDs or tags that the ore can spawn in
      '#minecraft:base_stone_overworld' // Default behavior - ores spawn in all stone types
    ]
    
    ore.biomes.blacklist = true // Inverts biome whitelist
    ore.biomes.values = [ // Biomes this ore can spawn in
      'minecraft:plains', // Biome ID
      '#nether' // OR #category, see list of categories below
    ]
    
    ore.clusterMinSize = 5 // Min blocks per cluster (currently ignored, will be implemented later, it's always 1)
    ore.clusterMaxSize = 9 // Max blocks per cluster
    ore.clusterCount = 30 // Clusters per chunk
    ore.minHeight = 0 // Min Y ore spawns in
    ore.maxHeight = 64 // Max Y ore spawns in
    ore.squared = true // Adds random value to X and Z between 0 and 16. Recommended to be true
    // ore.chance = 4 // Spawns the ore every ~4 chunks. You usually combine this with clusterCount = 1 for rare ores
  })
  
  event.addSpawn(spawn => { // Create new entity spawn
    spawn.category = 'creature' // Category, can be one of 'creature', 'monster', 'ambient', 'water_creature' or 'water_ambient'
    spawn.entity = 'minecraft:pig' // Entity ID
    spawn.weight = 10 // Weight
    spawn.minCount = 4 // Min entities per group
    spawn.maxCount = 4 // Max entities per group
  })
})

All values are optional. All feature types have biomes field like addOre example

Valid biome categories (#category):

This is the order vanilla worldgen happens:

  1. raw_generation
  2. lakes
  3. local_modifications
  4. underground_structures
  5. surface_structures
  6. strongholds
  7. underground_ores
  8. underground_decoration
  9. vegetal_decoration
  10. top_layer_modification

It's possible you may not be able to generate some things in their layer, like ores in dirt, because dirt hasn't spawned yet. So you may have to change the layer by calling ore.worldgenLayer = 'top_layer_modification'. But this is not recommended.

If you want to remove things, see this event.

Events

WorldgenRemoveEventJS

For more information on biomes field, see worldgen.add event page.

onEvent('worldgen.remove', event => {
  event.removeOres(ores => {
    ores.blocks = [ 'minecraft:coal_ore', 'minecraft:iron_ore' ] // Removes coal and iron ore
    ores.biomes.values = [ 'minecraft:plains' ] // Removes it only from plains biomes
  })
  
  event.removeSpawnsByID(spawns => {
    spawns.entities.values = [
      'minecraft:cow',
      'minecraft:chicken',
      'minecraft:pig',
      'minecraft:zombie'
    ]
  })
  
  event.removeSpawnsByCategory(spawns => {
    spawns.biomes.values = [
      'minecraft:plains'
    ]
    spawns.categories.values = [
      'monster'
    ]
  })
})

If something isn't removing, you may try to remove it "manually" by first printing all features (this will spam your console a lot, I suggest reading logs/kubejs/startup.txt) and then removing them by ID where possible.

onEvent('worldgen.remove', event => {
  // May be one of the decoration types/levels described in worldgen.add docs
  // But ores are *most likely* to be generated in this one
  event.printFeatures('underground_ores')
})
onEvent('worldgen.remove', event => {
  event.removeFeatureById('underground_ores', 'mekanism:ore_copper')
})
Events

ItemTooltipEventJS

onEvent('item.tooltip', tooltip => {
  // Add tooltip to all of these items
  tooltip.add(['quark:backpack', 'quark:magnet', 'quark:crate'], 'Added by Quark Oddities')
  // You can also use any ingredient except #tag
  tooltip.add(/refinedstorage:red_/, 'Can be any color')
  // Multiple lines with an array []. You can also escape ' by using other type of quotation marks
  tooltip.add('thermal:latex_bucket', ["Not equivalent to Industrial Foregoing's Latex", 'Line 2'])
  
  tooltip.addAdvanced('thermal:latex_bucket', (item, advanced, text) => {
    text.add(0, Text.of('Hello')) // Adds text in first line, replacing title. If you want 2nd line, the index must be 1
  })
})

Classes

Available fields and methods and examples on how to use them

Classes

Object

Parent class of all Java objects. 

Parent

None (and itself at the same time, don't question it)

Variables and Functions

Name Type Info
toString() String Tag collection type.
equals(Object other) boolean Checks equality with another object.
hashCode() int Hash code of this object. It is used to optimize maps and other things, should never be used for object equality.
class Class Object's type/class.
Classes

String

Class of string objects, such as "abc" (and in JS 'abc' works as well) 

Parent

Object

Variables and Functions

Name Type Info
empty boolean Returns if string is empty a.k.a string === ''
toLowerCase() String Returns a copy of this string, but with all characters in upper case
toUpperCase() String Returns a copy of this string, but with all characters in lower case
equalsIgnoseCase(String other) boolean Hash code of this object. It is used to optimize maps and other things, should never be used for object equality.
length() int Number of characters
charAt(int index) char Single character at index
Classes

Primitive Types

Information

Primitive types are objects that don't have a real class and don't inherit methods from Object.

All primitive types

Type Java class Info
void Void No type
byte Byte 8 bit decimal number.
short Short 16 bit decimal number.
int Integer 32 bit decimal number, most common decimal type.
long Long 64 bit decimal number.
float Float 32 bit floating point number.
double Double 64 bit floating point number.
char Character Single character in String such as 'a' or '-'.
boolean Boolean Only true and false values. Can be checked in if function without comparing to true, as if (x) or if (!x) instead of if (x == true) or if (x == false).

Global

Constants, classes and functions

Examples

Example scripts for various things you can do with KubeJS

Examples

KubeJS UI

You can also always look at existing modpack using KubeJS UI to see how they do it

events.listen('ui.main_menu', event => {
  event.replace(ui => {
    //ui.background('kubejsui:textures/example_background.png')
    ui.tilingBackground('kubejsui:textures/example_background.png', 256)
    ui.minecraftLogo(30)
    
    ui.button(b => {
      b.name = 'Test'
      b.x = 10
      b.y = 10
      b.action = 'minecraft:singleplayer'
    })
    
    ui.button(b => {
      b.name = 'Test but in bottom right corner'
      b.x = ui.width - b.width - 10
      b.y = ui.height - b.height - 10
      b.action = 'https://feed-the-beast.com/'
    })
    
    ui.label(l => {
      l.name = text.yellow('FTB Stranded')
      l.x = 2
      l.y = ui.height - 12
      l.action = 'https://feed-the-beast.com/'
    })

    ui.image(i => {
      i.x = (ui.width - 40) / 2
      i.y = (ui.height - 30) / 2
      i.width = 40
      i.height = 30
      i.action = 'https://feed-the-beast.com/'
    })
    
    ui.label(l => {
      l.name = text.aqua('Large label')
      l.x = 100
      l.y = ui.height - 20
      l.height = 15
      l.shadow = true
    })
  })
})
Examples

Custom Items

This is a startup_scripts/ event

// Listen to item registry event
onEvent('item.registry', event => {
  // The texture for this item has to be placed in kubejs/assets/kubejs/textures/item/test_item.png
  // If you want a custom item model, you can create one in Blockbench and put it in kubejs/assets/kubejs/models/item/test_item.json
  event.create('test_item').displayName('Test Item')
})

Other methods item builder supports: [you can chain these methods after displayName()]

Valid item types:

Valid item tiers:

Valid group/creative tab IDs:

Creating custom tool and armor tiers

All values are optional and by default are based on iron tier

onEvent('item.registry.tool_tiers', event => {
  event.add('tier_id', tier => {
    tier.uses = 250
    tier.speed = 6.0
    tier.attackDamageBonus = 2.0
    tier.level = 2
    tier.enchantmentValue = 14
    tier.repairIngredient = '#forge:ingots/iron'
  })
})
onEvent('item.registry.armor_tiers', event => {
  // Slot indicies are [HEAD, BODY, LEGS, FEET]
  event.add('tier_id', tier => {
    tier.durabilityMultiplier = 15 // Each slot will be multiplied with [13, 15, 16, 11]
    tier.slotProtections = [2, 5, 6, 2]
    tier.enchantmentValue = 9
    tier.equipSound = 'minecraft:item.armor.equip_iron'
    tier.repairIngredient = '#forge:ingots/iron'
    tier.toughness = 0.0 // diamond has 2.0, netherite 3.0
    tier.knockbackResistance = 0.0
  })
})
Examples

Custom Blocks

materialThis is a startup script.

onEvent('block.registry', event => {
  event.create('test_block').material('glass').hardness(0.5).displayName('Test Block')
})


The texture for this block has to be placed in kubejs/assets/kubejs/textures/block/test_block.png.
If you want a custom block model, you can create one in Blockbench and put it in kubejs/assets/kubejs/models/block/test_block.json.

 

List of available materials - to change break/walk sounds and to change some properties (tool used to mine, etc):

Material
air
wood
rock
iron
organic

earth

water
lava
leaves
plants
sponge
wool
sand
glass
tnt
coral
ice
snow
clay
groud
dragon_egg
portal
cake
web
slime
honey
berry_bush
lantern

 

Other methods block builder supports: [you can chain these methods after displayName()]

Event callbacks:

RandomTickEvent:

Types
Examples

Loot Table Modification

onEvent('block.loot_tables', event => {
  event.addSimpleBlock('minecraft:dirt', 'minecraft:red_sand')
})
onEvent('block.loot_tables', event => {
  event.addSimpleBlock('minecraft:dirt') // To drop itself (fix broken blocks)
  event.addSimpleBlock(/minecraft:.*_ore/, 'minecraft:red_sand') // To drop a different item
})
onEvent('block.loot_tables', event => {
  event.addBlock('minecraft:dirt', table => { // Build loot table manually
    table.addPool(pool => {
      pool.rolls = 1 // fixed
      // pool.rolls = [4, 6] // or {min: 4, max: 6} // uniform
      // pool.rolls = {n: 4, p: 0.3} // binominal
      pool.survivesExplosion()
      pool.addItem('minecraft:dirt')
      pool.addItem('minecraft:dirt', 40) // 40 = weight
      pool.addItem('minecraft:dirt', 40, [4, 8]) // [4-8] = count modifier, uses same syntax as rolls
      // pool.addCondition({json condition, see vanilla wiki})
      // pool.addEntry({json entry, see vanilla wiki for non-items})
    })
  })
})

Example from Factorial: (adds 1-3 leaves dropped from all Leaves blocks, 4-8 logs from all log and wood blocks and 4-8 stone from Stone, Cobblestone, Andesite, Diorite and Granite)

onEvent('block.loot_tables', event => {
	event.addBlock(/minecraft:.*_leaves/, table => {
		table.addPool(pool => {
			pool.survivesExplosion()
			pool.addItem('factorial:leaf', 1, [1, 3])
		})
	})

	event.addBlock(/minecraft:.*_(log|wood)/, table => {
		table.addPool(pool => {
			pool.survivesExplosion()
			pool.addItem('factorial:wood', 1, [4, 8])
		})
	})

	event.addBlock([
		'minecraft:stone',
		'minecraft:cobblestone',
		'minecraft:andesite',
		'minecraft:diorite',
		'minecraft:granite'
	], table => {
		table.addPool(pool => {
			pool.rolls = [4, 8] // Roll the pool instead of individual items
			pool.survivesExplosion()
			pool.addItem('factorial:stone', 1)
		})
	})
})

You can also modify existing loot tables to add items to them:

onEvent('block.loot_tables', event => {
  // all dirt blocks have a 50% chance to drop an enchanted diamond sword named "test"
  event.modifyBlock(/^minecraft:.*dirt/, table => {
    table.addPool(pool => {
      pool.addItem('minecraft:diamond_sword').randomChance(0.5).enchantWithLevels(1, true).name(Text.of('Test').blue())
    })
  })
})

Other loot table types work too:

onEvent('entity.loot_tables', event => {
  // Zombies will drop 5 of either carrot (25% chance) or apple (75% chance)
  event.addEntity('minecraft:zombie', table => {
    table.addPool(pool => {
      pool.rolls = 5
      pool.addItem('minecraft:carrot', 1)
      pool.addItem('minecraft:apple', 3)
    })
  })
})

Supported table types:

Event ID Method name
generic.loot_tables addGeneric
block.loot_tables addBlock
entity.loot_tables addEntity
gift.loot_tables addGift
fishing.loot_tables addFishing
chest.loot_tables addChest
Examples

Item Modification

item.modification event is a startup script event that allows you to change properties of existing items

onEvent('item.modification', event => {
  event.modify('minecraft:ender_pearl', item => {
    item.maxStackSize = 64
    item.fireResistant = true
  })
})

All available properties:

Examples

Block Modification

block.modification event is a startup script event that allows you to change properties of existing blocks

onEvent('block.modification', event => {
  event.modify('minecraft:stone', block => {
    block.destroySpeed = 0.1
    block.hasCollision = false
  })
})

All available properties:

Examples

JEI Integration

Sub-types

onEvent('jei.subtypes', event => {
  event.useNBT('example:item')
  event.userNBTKey('example:item', 'type')
})

Hide Items & Fluids

onEvent('jei.hide.items', event => {
  event.hide('example:ingredient')
})

onEvent('jei.hide.fluids', event => {
  event.hide('example:fluid')
})

Add Items & Fluids

onEvent('jei.add.items', event => {
  event.add(Item.of('example:item', {test: 123}))
})

onEvent('jei.add.fluids', event => {
  event.add('example:fluid')
})

Add Information

onEvent('jei.information', event => {
  event.add('example:ingredient', ['Line 1', 'Line 2'])
})
Examples

REI Integration (Fabric Only)

Hide Items

onEvent('rei.hide.items', event => {
  event.hide('example:ingredient')
})

Add Items

onEvent('rei.add.items', event => {
  event.add(item.of('example:item').nbt({test: 123}))
})

Add Information

onEvent('rei.information', event => {
  event.add('example:ingredient', 'Title', ['Line 1', 'Line 2'])
})
Examples

FTB Quests Integration

onEvent('ftbquests.custom_task.75381f79', event => {
  log.info('Custom task!')
  event.checkTimer = 20
  event.check = (task, player) => {
    if (player.world.daytime && player.world.raining) {
      task.progress++
    }
  }
})

onEvent('ftbquests.custom_reward.e4f76908', event => {
  log.info('Custom reward!')
  event.player.tell('Hello!')
})

// specific object completion
onEvent('ftbquests.completed.d4f36905', event => {
  if (event.player) {
    event.notifiedPlayers.tell(Text.of(`${event.player.name} completed... something!`).green())
  }
})

// generic 'quest' object completion. Note: There isnt actually a way to get reliable title on server side, so dont use event.object.title
onEvent('ftbquests.completed', event => {
  if (event.player && event.object.objectType.id === 'quest') {
    event.notifiedPlayers.tell(Text.of(`${event.player.name} completed a quest!`).blue())
  }
})

// object with tag 'ding' completion
onEvent('ftbquests.completed.ding', event => {
  event.onlineMembers.playSound('entity.experience_orb.pickup')
})

onEvent('entity.death', event => {
  if(event.server
  && event.source.actual
  && event.source.actual.player
  && event.source.actual.mainHandItem.id === 'minecraft:wooden_sword'
  && event.entity.type === 'minecraft:zombie') {
    event.source.actual.data.ftbquests.addProgress('12345678', 1)
  }
})
Examples

Reflection / Java access

Very limited reflection is possible, but is not recommended. Use it in cases, when mod doesn't have integration.

// side: client
// This is a startup script. This script changes a java variable in ProjectE mod by accessing code directly

// Listen to post-init event, after all mods have loaded
onEvent('postinit', event => {
  // Loads Java class field
  var PECore = java('moze_intel.projecte.PECore')
  // Changes public static boolean DEV_ENVIRONMENT of PECore class to false
  PECore.DEV_ENVIRONMENT = false
})
Examples

Painter API

About

Painter API allows you to draw things on the screen, both from server and directly from client. This can allow you to create widgets from server side or effects on screen or in world from client side.

Currently it doesn't support any input, but in future, in-game menus or even GUIs similar to Source engine ones will be supported.

Paintable objects are created from NBT/Json objects and all have an id. If id isn't provided, a random one will be generated. Objects x and z are absolute positions based on screen, but you can align elements in one of the corners of screen. You can bulk add multiple objects in one json object. All properties are optional, but obviously some you should almost always override like size and position for rectangles.

paint({...}) is based on upsert principle - if object doesn't exist it will create it (if the object also contains valid type), otherwise, update existing:

You can bulk update/create multiple things in same object:

You can remove object with remove: true, bulk remove multiple objects or remove all objects:

These methods have command alternatives:

If the object is re-occuring, it's recommended to create objects at login with all of its static properties and visible: false, then update it later to unhide it. Painter objects will be cleared when players leave world/server, if its persistent, then it must be re-added at login every time.

Currently available objects

Underlined objects are not something you can create

Root

(available for all objects)

Screen Object

(available for all screen / 2D objects)

rectangle
gradient
text
World Object

Properties

Available Unit variables

Examples

onEvent('player.logged_in', event => {
	event.player.paint({
		example_rectangle: {
			type: 'rectangle',
			x: 10,
			y: 10,
			w: 50,
			h: 20,
			color: '#00FF00',
			draw: 'always'
		},
		last_message: {
			type: 'text',
			text: 'No last message',
			scale: 1.5,
			x: -4,
			y: -4,
			alignX: 'right',
			alignY: 'bottom',
			draw: 'always'
		}
	})
})

onEvent('player.chat', event => {
	// Updates example_rectangle x value and last_message text value to last message + contents from event
	event.player.paint({example_rectangle: {x: '(sin((time() * 1.1)) * (($screenW - 32) / 2))', w: 32, h: 32, alignX: 'center', texture: 'kubejs:textures/item/diamond_ore.png'}})
	event.player.paint({last_message: {text: `Last message: ${event.message}`}})
	// Bulk update, this is the same code as 2 lines above, you can use whichever you like better
	// event.player.paint({example_rectangle: {x: 120}, last_message: {text: `Last message: ${event.message}`}})
	event.player.paint({lava: {type: 'atlas_texture', texture: 'minecraft:block/lava_flow'}})
})