0.17 ➝ 0.18
Minecraft version
Updated to 1.21.4
Addons are now loaded as Paper plugins
Addons are now loaded as Paper plugins. This includes some changes to the build setup, so please check out the updated Nova-Addon-Template.
This also means that id and name are no longer separate. Instead, the lowercase name of the plugin is now also the id. Please verify that this does not result in a different id than before.
The nova gradle plugin will automatically generate a paper-plugin.yml
, as well as loader- bootstrapper- and plugin
main class for you. You can also create your own loader- bootstrapper- and main classes. If you do so, please define them
in the configuration of the nova gradle plugin, and NOT in the paper-plugin.yml
, as the gradle plugin needs to inject some
code into them. Also note that defining a custom loader will disable the library loading functionality via the
libraryLoader
dependency configuration.
Nova will automatically move configs, recipes, and other data from plugins/Nova/...
to the data folder of your plugin.
Early initialization
All PRE_WORLD initialization phases are now earlier (during Bootstrap phase).
At this time, most Bukkit classes are not usable yet, as they depend on a not yet existing MinecraftServer
instance.
Common exceptions that may indicate that you're trying to access things too early are:
java.lang.IllegalStateException: Trying to access unbound value ...
Caused by: java.lang.NullPointerException: Cannot invoke ... because "org.bukkit.Bukkit.server" is null
Item models
With 1.21.4, Mojang has introduced Item model definitions. The existing model selection functionality of items and blocks has changed to accommodate this new functionality. Items can no longer have multiple (named) models, as this is now achievable through the new custom model data component.
See Item Model Definitions for a full tutorial on Nova's item model definition builder.
Basically,
becomes
The item model definition builder also offers numberedModels
and rangedModels
functions as replacements for
selectModel(IntRange, ...)
. These can then be accessed via custom model data.
Block models
Block model selection has been simplified a bit and is no longer as nested as before:
(same for entityBacked
)
Item model definitions for block models
You can also use item model definitions for block models via entityItemBacked
.
This allows you to use minecraft:special
models, such as chests or signs for blocks.
Default item/block behavior factory companion objects
The companion objects of the default item/block behaviors that allow configuration are no longer item/block behavior factories. This was done to merge the "hardcoded" and "configurable" ways to create instances of these item behaviors.
Basically, this means that behaviors(Tool, Damageable, Enchantable)
becomes behaviors(Tool(), Damageable(), Enchantable()
. If you don't change any of the default parameters, all
values will always be read from the config. If you specify any parameter, a config entry will not be necessary, but existing
config entries will still override the hardcoded value.
Equipment
In 1.21.2, Mojang introduced custom equipment. Following this naming, ArmorRegistry
is now EquipmentRegistry
and
offers new functionality, such as custom armor for non-player entities.
Animated armor now also works for all clients, including those running shader mods.
@Init(stage = InitStage.PRE_PACK)
object Equipment : EquipmentRegistry by Machines.registry {
val STAR = equipment("star") {
humanoid {
layer {
texture("star")
emissivityMap("star_emissivity_map")
}
}
humanoidLeggings { // (1)!
layer {
texture("star")
emissivityMap("star_emissivity_map")
}
}
}
}
- Minecraft separates
humanoid
andhumanoid_leggins
into two categories, I don't know why either.
Wearable
is now named Equippable
Gui textures
The gui texture builder has been simplified:
Providers
Providers have been reworked. They are now thread-safe and lazy both up- and downstream.
- The packages
xyz.xenondevs.commons.provider.immutable
andxyz.xenondevs.commons.provider.mutable
have been collapsed into the parent packagexyz.xenondevs.commons.provider
. Provider#update
has been removed. Similar functionality can be replicated by creating a mutable provider and callingset
on it.- Extension functions for collection-mapping functionality, such as
flatMap
, have been renamed (toflatMapCollection
) in order to make room forflatMap((T) -> Provider<R>)
. Provider.orElse(MutableProvider)
has been removed as it was not compatible with lazy upstream propagation (Provider.orElse(Provider)
still exists).- Nova's
ConfigProvider
no longer exists. Instead, equivalent extension functions are now available onProvider<ConfigurationNode>
.
InvUI 2.0.0-alpha.1
InvUI has been updated to a pre-release version of 2.0.0.
See the GitHub release page for a list of breaking changes and new features.
Reactive API
InvUI now offers an experimental reactive API in the invui-kotlin
module.
This includes extension functions for Item.Builder#setItemProvider
, PagedGui#setContent
, ..., that accept
a Provider
. The item/gui/window will be automatically updated when the provider's value changes, removing the need
for manual notifyWindows
calls.
Reactive API example
Since the InvUI documentation is not updated yet, here is an example on how to use the reactive API:
val num: MutableProvider<Int> = mutableProvider(0) // stores the number of clicks on the '#' item
val query: MutableProvider<String> = mutableProvider("") // stores the anvil window text
fun example(player: Player) {
// example item that shows click count
Structure.addGlobalIngredient('#', Item.builder()
.setItemProvider(num) { num -> ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setName("Count: $num") } // item updates based on num provider
.addClickHandler { _, _ -> num.set(num.get() + 1) }) // click increments num
// page buttons
Structure.addGlobalIngredient('>', BoundItem.pagedGui()
.setItemProvider { _, gui -> ItemBuilder(Material.ARROW).setName("Go to page ${gui.page + 2}/${gui.pageAmount}") }
.addClickHandler { _, gui, click -> if (click.clickType == ClickType.LEFT) gui.page++ })
Structure.addGlobalIngredient('<', BoundItem.pagedGui()
.setItemProvider { _, gui -> ItemBuilder(Material.ARROW).setName("Go to page ${gui.page}/${gui.pageAmount}") }
.addClickHandler { _, gui, click -> if (click.clickType == ClickType.LEFT) gui.page-- })
// marker
Structure.addGlobalIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
AnvilWindow.split()
.setTitle("InvUI")
.addRenameHandler(query) // automatically writes text into query provider
.setUpperGui(
Gui.normal()
.setStructure("###")
.addIngredient('#', Item.simple(ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setName(""))) // this item does nothing
)
.setLowerGui(PagedGui.items()
.setStructure(
"x x x x x x x x x",
"x x x x x x x x x",
"x x x x x x x x x",
"# # < # # # > # #")
.setContent(query) { query -> // content updates based on query provider
Material.entries
.filter { !it.isLegacy && it.isItem && it.name.contains(query, true) }
.map { Item.simple(ItemStack(it)) }
})
.open(player)
}
And this is what it looks like:
ResourceType in ResourcePath
ResourcePath
now includes a ResourceType
parameter to specify the type (and path / extension) of the resource.
Other changes
- All usages of
net.minecraft.resources.ResourceLocation
have been placed withnet.kyori.adventure.key.Key