KubeJS

FAQ

What does this mod do?

This mod lets you create scripts in JavaScript language to manage your server, add new blocks and items, change recipes, add custom handlers for quest mods and more!

How to use it?

Run the game with mod installed once. It should generate kubejs folder in your minecraft directory with example scripts. startup scripts only get run once when the game loads. Those are for registering items, setting up configs etc. Server/data scripts are loaded when you join a world, those are better for things like clearlag, displaying custom login messages and world/player related things in general.

I don't know JavaScript

There's examples and pre-made scripts on kubejs.latvian.dev.

Can I reload scripts?

Yes, use /reload. Note: Not everything is reloadable. Some things require you to restart game, some only world, some work on fly.

How does this mod work?

It uses Nashorn, built-in JavaScript parser to read scripts, shipped with Java 8 and above. There shouldn't be a case where this doesn't exist in your Minecraft, but if that's the case, please report!

Ok, but what if it.. doesn't work?

You can report issues here.

I have more questions/suggestions!

You can find me on Discord, in #kubejs-and-code chat channel.

Event Table

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

Events

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

Classes

Available fields and methods and examples on how to use them

Global

Constants, classes and functions

Examples

Example scripts for various things you can do with KubeJS

Examples

Recipes

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

// Enable recipe logging, off by default
server.logAddedRecipes = true
server.logRemovedRecipes = true

// Listen to server recipe event
events.listen('recipes', function (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({}) // Deletes all recipes
  event.remove({id: 'minecraft:glowstone'}) // Removes data/minecraft/recipes/glowstone.json
  event.remove({input: '#forge:dusts/redstone'}) // Removes all recipes where input is Redstone Dust tag
  event.remove({output: '#minecraft:wool'}) // Removes all recipes where output is Wool tag
  event.remove({mod: 'quartzchests'}) // Remove all recipes from Quartz Chests mod
  event.remove({type: 'minecraft:campfire_cooking'}) // Remove all campfire cooking recipes

  // Add shaped recipe for 3 Stone from 8 Sponge in chest shape
  // (Shortcut for event.recipes.minecraft.crafting_shaped)
  event.shaped(item.of('minecraft:stone', 3), [
    'SSS',
    'S S',
    'SSS'
  ], {
    S: 'minecraft:sponge'
  })

  // Add shapeless recipe for 4 Cobblestone from 1 Stone and 1 Glowstone
  // (Shortcut for event.recipes.minecraft.crafting_shapeless)
  event.shapeless(item.of('minecraft:cobblestone', 4), ['minecraft:stone', '#forge:dusts/glowstone'])

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

  // Add Furnace recipe for Golden Apple to 3 Carrots
  // (Shortcut for event.recipes.minecraft.smelting)
  event.smelting(item.of('minecraft:carrot', 2), 'minecraft:golden_apple')

  // Add similar recipes for Blast Furnace, Smoker and Campfire
  event.recipes.minecraft.blasting(item.of('minecraft:apple', 3), 'minecraft:golden_apple')
  event.recipes.minecraft.smoking(item.of('minecraft:apple', 5), 'minecraft:golden_apple')
  event.recipes.minecraft.campfire_cooking('minecraft:apple', 'minecraft:golden_apple')
  // You can also add .xp(1.0) at end of any smelting recipe to change given XP

  // Create a variable for function and use that to make things shorter
  var s = event.recipes.minecraft.smelting
  s('minecraft:blue_dye', '#forge:gems/lapis')
  s('minecraft:black_dye', 'minecraft:ink_sac')
  s('minecraft:white_dye', 'minecraft:bone_meal')

  // Add Create's crushing recipe, Apple to Apple + 50% Carrot
  event.recipes.create.crushing(['minecraft:apple', item.of('minecraft:carrot').chance(0.5)], 'minecraft:apple', 100)

  // If you use {} as only argument, it will be using vanilla Json syntax
  // You can add recipe to any recipe handler that uses vanilla recipe system or isn't supported by KubeJS
  event.recipes.create.crushing({
    ingredients: [
      { item: 'minecraft:carrot' }
    ],
    results: [
      { item: 'minecraft:apple', count: 1, chance: 0.5 },
      { item: 'minecraft:carrot', count: 1 }
    ],
    processingTime: 100
  })
  
  // 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')
})

// Listen to item tag event
events.listen('item.tags', function (event) {
  // Add #forge:cobblestone tag to Diamond Ore
  event.get('forge:cobblestone').add('minecraft:diamond_ore')
  
  // Remove Cobblestone from #forge:cobblestone tag
  event.get('forge:cobblestone').remove('minecraft:cobblestone')
})
Examples

Custom Blocks

This is a startup script.

events.listen('block.registry', function (event) {
  event.create('test_block').material(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.

Examples

Reflection

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
events.listen('postinit', function (event) {
  // Loads Java class field
  var devEnvField = utils.field('moze_intel.projecte.PECore', 'DEV_ENVIRONMENT')
  // Changes public static boolean DEV_ENVIRONMENT of PECore class to false
  devEnvField.staticSet(false)
})
Examples

Chat Event

// This script is peak of human evolution. Whenever someone says "Creeper" in chat, it replies with "Aw man"

// Listen to chat event
events.listen('player.chat', function (event) {
  // Check if message equals creeper, ignoring case
  if (event.message.trim().equalsIgnoreCase('creeper')) {
    // Schedule task in 1 tick, because if you reply immidiently, it will print before player's message
    event.server.scheduleInTicks(1, event.server, function (callback) {
      // Tell everyone Aw man, colored green. Callback data is the server
      callback.data.tell(text.green('Aw man'))
    })
  }
})
Examples

Network Packets

This script shows how to use network packets:

// Listen to a player event, in this case item right-click
events.listen('item.right_click', function (event) {
  // Check if item was right-clicked on client or server side
  if (event.player.isServer()) {
    // Send data {test: 123} to channel "test_channel_1". Channel ID can be any string, but it's recommended to keep it to snake_case [a-z_0-9].
    // Receiving side will either be client (because its fired from server).
    event.player.sendData('test_channel_1', {
      test: 123
    })
  } else {
    // It's not required to use a different channel ID, but it's recommended.
    // Receiving side will either be server (because its fired from client).
    event.player.sendData('test_channel_2', {
      test: 456
    })
  }
})

// Listen to event that gets fired when network packet is received from server.
events.listen('player.data_from_server.test_channel_1', function (event) {
  log.info(event.data.get('test').asInt()) // Prints 123
})

// Listen to event that gets fired when network packet is received from client.
events.listen('player.data_from_client.test_channel_2', function (event) {
  log.info(event.data.get('test').asInt()) // Prints 456
})
Examples

Starting Items

This script adds items on first time player joins, checking gamestages

Requires GameStages mod!

// Listen to player login event
events.listen('player.logged_in', function (event) {
  // Check if player doesn't have "starting_items" gamestage yet
  if (!event.hasGameStage('starting_items')) {
    // Add the gamestage
    event.addGameStage('starting_items')
    // Give some items to player
    event.player.give('minecraft:stone_sword')
    event.player.give({ item: 'minecraft:stone_pickaxe', data: 10 })
    event.player.give({ item: 'minecraft:apple', count: 30 })
  }
})
Examples

Custom Items

// Listen to block registry event
events.listen('item.registry', function (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')
})
Examples

FTB Utilities Rank Promotions

With this script you can have FTB Utilities roles that change over time.

Is for 1.12 only. Requires FTB Utilities.

events.listen('player.tick', function (event) {
  // This check happens every 20 ticks, a.k.a every second
  if (event.player.server && event.player.ticksExisted % 20 === 0) {
    var rank = event.player.data.ftbutilities.rank
    events.post('test_event', {testValue: rank.id})
    var newRank = ftbutilities.getRank(rank.getPermission('promotion.next'))

    if (newRank) {
      var timePlayed = event.player.stats.get('stat.playOneMinute') / 20 // Seconds player has been on server
      var timeRequired = newRank.getPermissionValue('promotion.timer').getInt()

      if (timeRequired > 0 && timePlayed >= timeRequired && rank.addParent(newRank)) {
        if (!events.postCancellable('ftbutilities.rank.promoted.' + newRank.id, {'player': event.player, 'rank': newRank})) {
          event.player.tell('You have been promoted to ' + newRank.getPermission('promotion.name') + '!')
        }
        ftbutilities.saveRanks()
      }
    }
  }
})

// When player gets promoted to 'trusted' rank, give them gold ingot (uncomment the line)
events.listen('ftbutilities.rank.promoted.trusted', function (event) {
  // event.data.player.give('minecraft:gold_ingot')
})

3 example roles in ranks.txt:

[player]
power: 1
default_player_rank: true
promotion.name: Player
promotion.next: newcomer
promotion.timer: 5
command.ftbutilities.rtp: false
command.ftbutilities.home: false

[newcomer]
power: 5
promotion.name: Newcomer
promotion.next: regular
promotion.timer: 15
ftbutilities.chat.name_format: <&aNewcomer &r{name}>
command.ftbutilities.rtp: true

[regular]
power: 10
promotion.name: Regular
promotion.next: trusted
promotion.timer: 30
ftbutilities.chat.name_format: <&9Regular &r{name}>
command.ftbutilities.home: true

After 5 seconds of play time, player will be promoted to newcomer.
After 15 seconds (or 10 since previous role) they will be promoted to regular.
After 30 seconds (or 15 since previous role) they will be promoted to trusted, etc.

Examples

Clearlag

This script removes all items from world every 30 minutes. Only works in 1.12.

// Create item whitelist filter that won't be deleted with clearlag
var whitelist = ingredient.matchAny([
  'minecraft:diamond', // Adds diamond to whitelist
  'minecraft:gold_ingot',
  ingredient.mod('tinkersconstruct'), // Adds all items from tinkerscontruct to whitelist
  'minecraft:emerald'
])

// Create variable for last clearlag result
var lastClearLagResult = utils.newList()
// Create variable for total number of items
var lastTotalClearLagResult = utils.newCountingMap()

// Create new function that clears lag
function clearLag (server) {
  // Get a list of all entities on server with filter that only returns items
  var itemList = server.getEntities('@e[type=item]')
  // Create new local map for item counters
  var lastResult = utils.newCountingMap()
  // Clear old result lists
  lastClearLagResult.clear()
  lastTotalClearLagResult.clear()
  // Iterate over each entity in itemList and add item counters
  itemList.forEach(function (entity) {
    if (!whitelist.test(entity.item)) {
      // Get the name of item
      var key = entity.item.name
      // Add to entity count
      lastResult.add(key, 1)
      // Add to total item count
      lastTotalClearLagResult.add(key, entity.item.count)
      // Kill the item entity
      entity.kill()
    }
  })

  // Update and sort last result list
  lastClearLagResult.addAll(lastResult.entries)
  lastClearLagResult.sort(null)

  // Tell everyone how many items will be removed
  server.tell([
    text.lightPurple('[ClearLag]'),
    ' Removed ',
    lastTotalClearLagResult.totalCount,
    ' items. ',
    text.yellow('Click here').click('command:/clearlagresults'),
    ' for results.'
  ])
}

// Listen for server load event
events.listen('server.load', function (event) {
  // Log message in console
  event.server.tell([ text.lightPurple('[ClearLag]'), ' Timer started, clearing lag in 30 minutes!' ])
  // Schedule new task in 30 minutes
  event.server.schedule(MINUTE * 30, event.server, function (callback) {
    // Tell everyone on server that items will be removed
    callback.data.tell([ text.lightPurple('[ClearLag]'), ' Removing all items on ground in one minute!' ])
    // Schedule a subtask that will clear items in one minute
    callback.data.schedule(MINUTE, callback.data, function (callback2) {
      clearLag(callback2.data)
    })
    // Re-schedule this task for another 30 minutes (endless loop)
    callback.reschedule()
  })
})

// Register commands
events.listen('command.registry', function (event) {
  // Register new OP command /clearlag, that instantly runs clearlag
  event
    .create('clearlag')
    .op()
    .execute(function (sender, args) {
      clearLag(sender.server)
    })
    .add()

  // Register new non-OP command /clearlagresults, that displays stats of all removed items from previous /clearlag
  event
    .create('clearlagresults')
    .execute(function (sender, args) {
      sender.tell([ text.lightPurple('[ClearLag]'), ' Last clearlag results:' ])

      lastClearLagResult.forEach(function (entry) {
        var total = lastTotalClearLagResult.get(entry.key)

        if (entry.value == total) {
          sender.tell([ text.gold(entry.key), ': ', text.red(entry.value) ])
        } else {
          sender.tell([ text.gold(entry.key), ': ', text.red(entry.value), ' entities, ', text.red(total), ' total' ])
        }
      })
    })
    .add()
})