Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
366418e
RECIPES VOLUME 1: THE PROPHECY
LordIdra Jun 9, 2026
f092edd
RECIPES VOLUME 2: EMERGENCE
LordIdra Jun 9, 2026
faf3c02
RECIPES VOLUME 3: CONVERSION
JustAHuman-xD Jun 9, 2026
5f31ff0
RECIPES VOLUME 4: TRANSMUTATION
LordIdra Jun 9, 2026
0b7a207
RECIPES VOLUME 5: DOPPELGANGERS
JustAHuman-xD Jun 9, 2026
9c9de3b
RECIPES VOLUME 6: EVOLUTION
LordIdra Jun 9, 2026
a7b8887
Merge branch 'idra/recipe-rewrite' of github.com:pylonmc/rebar into i…
LordIdra Jun 9, 2026
b0faf54
RECIPES VOLUME 7: REPAIR
JustAHuman-xD Jun 9, 2026
58c46a2
RECIPES VOLUME 8: NEW PERSPECTIVES
LordIdra Jun 9, 2026
d955a8a
Merge branch 'idra/recipe-rewrite' of github.com:pylonmc/rebar into i…
LordIdra Jun 9, 2026
506ded6
RECIPES VOLUME 8: REVELATIONS
JustAHuman-xD Jun 9, 2026
9cc2807
RECIPES VOLUME 8.5: COALESCENCE
LordIdra Jun 9, 2026
dc77f67
Merge remote-tracking branch 'origin/idra/recipe-rewrite' into idra/r…
LordIdra Jun 9, 2026
9cdaa9f
RECIPES VOLUME 9: ACCEPTANCE
JustAHuman-xD Jun 9, 2026
c9dadc0
RECIPES VOLUME 9.1: FLEXIBILITY
JustAHuman-xD Jun 9, 2026
197427c
RECIPES VOLUME 10: DEVASTATION
LordIdra Jun 9, 2026
d425f91
RECIPES VOLUME 10.1: THE SHEDDING
JustAHuman-xD Jun 9, 2026
93d9792
RECIPES VOLUME 10.5: WASTELANDS
JustAHuman-xD Jun 10, 2026
a82fb80
Revert "RECIPES VOLUME 10: DEVASTATION"
LordIdra Jun 10, 2026
f332838
RECIPES VOLUME 10.01: INTEMRISSION
LordIdra Jun 10, 2026
5c98bcb
RECIPES VOLUME 11: SERENITY
LordIdra Jun 10, 2026
ab2def0
RECIPES VOLUME 12: ENLIGHTENMENT
JustAHuman-xD Jun 10, 2026
ead6d94
RECIPES VOLUME 13: SERENITY
JustAHuman-xD Jun 10, 2026
6a0744b
docs improvements and stuff
LordIdra Jun 12, 2026
7ad25db
Merge branch 'idra/recipe-rewrite' into idra/docs-improvements-and-stuff
LordIdra Jun 12, 2026
7f6be6c
config adapter improvements
LordIdra Jun 13, 2026
b563fef
PR comments
LordIdra Jun 16, 2026
aea04d3
oh that's why I didn't do it
LordIdra Jun 16, 2026
fea84ce
apistatus internal
LordIdra Jun 16, 2026
414e98d
.of
LordIdra Jun 17, 2026
fbea0e1
fix rebar item javadoc
JustAHuman-xD Jun 17, 2026
9afff70
Merge pull request #841 from pylonmc/idra/docs-improvements-and-stuff
LordIdra Jun 18, 2026
999537b
Merge branch 'master' into idra/recipe-rewrite
JustAHuman-xD Jun 18, 2026
c37a5c0
RECIPES VOLUME 14.01: WAKING
JustAHuman-xD Jun 18, 2026
57c9751
RECIPES VOLUME 14.02: PARALYSIS
JustAHuman-xD Jun 18, 2026
f1a41d2
RECIPES VOLUME 14.03: CURE
JustAHuman-xD Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 124 additions & 3 deletions nms/src/main/kotlin/io/github/pylonmc/rebar/nms/NmsAccessorImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,14 @@ import kotlin.math.max
import kotlin.math.min
import com.mojang.datafixers.util.Pair as NmsPair
import net.minecraft.world.entity.EquipmentSlot as NmsEquipmentSlot

import net.minecraft.world.item.ItemStack as NmsItemStack
import net.minecraft.core.component.DataComponentType as NmsDataComponentType

/**
* Documentation is in [NmsAccessor].
*
* @see NmsAccessor
*/
@Suppress("unused")
object NmsAccessorImpl : NmsAccessor {

Expand Down Expand Up @@ -341,6 +348,11 @@ object NmsAccessorImpl : NmsAccessor {
}
}

fun getBukkitType(nmsType: NmsDataComponentType<*>): PaperDataComponentType<*, *>? {
val bukkitType = PaperDataComponentType.minecraftToBukkit(nmsType) as? PaperDataComponentType<*, *>
return if (bukkitType !is PaperDataComponentType.Unimplemented<*, *>) bukkitType else null
}

override fun getOverriddenTypes(itemStack: ItemStack): List<DataComponentType> {
val schema = RebarItemSchema.fromStack(itemStack)
val nmsStack = (itemStack as CraftItemStack).handle
Expand All @@ -350,10 +362,119 @@ object NmsAccessorImpl : NmsAccessor {
val types = mutableListOf<DataComponentType>()
for (type in nmsTemplate.components.keySet()) {
if (nmsTemplate.get(type) != nmsStack.get(type)) {
types.add(PaperDataComponentType.minecraftToBukkit(type))
types.add(getBukkitType(type) ?: continue)
}
}
return types
}
return nmsStack.componentsPatch.entrySet().mapNotNull { getBukkitType(it.key) }
}

fun <T: Any, NMS: Any> componentMatches(itemStack: NmsItemStack, type: PaperDataComponentType.ValuedImpl<T, NMS>, value: Any?): Boolean {
val nmsType = PaperDataComponentType.bukkitToMinecraft<NMS>(type)
val nmsValue = itemStack.get(nmsType)
if (nmsValue == value) {
return true
} else if (value == null || nmsValue == null) {
return false
}
return nmsStack.componentsPatch.entrySet().map { PaperDataComponentType.minecraftToBukkit(it.key) }

val adaptedValue = type.adapter.fromVanilla(nmsValue)
return adaptedValue == value
}

fun componentMatches(itemStack: NmsItemStack, type: PaperDataComponentType.NonValuedImpl<*, *>, hasValue: Boolean): Boolean {
val nmsType = PaperDataComponentType.bukkitToMinecraft<Unit>(type)
return itemStack.has(nmsType) == hasValue
}

fun <T: Any, NMS: Any> convertNmsValue(type: PaperDataComponentType<T, NMS>, nmsValue: Any): T {
return type.adapter.fromVanilla(nmsValue as NMS)
}

override fun overriddenComponents(itemStack: ItemStack, exact: Boolean): Map<DataComponentType, Any?> {
val nmsComponents = mutableMapOf<NmsDataComponentType<*>, Any?>()
val schema = RebarItemSchema.fromStack(itemStack)
val nmsStack = (itemStack as CraftItemStack).handle
if (schema != null && !exact) {
val template = schema.getOriginalTemplate()
val nmsTemplate = (template as CraftItemStack).handle
for (type in nmsTemplate.components.keySet()) {
val realValue = nmsStack.get(type)
if (nmsTemplate.get(type) != realValue) {
nmsComponents[type] = realValue
}
}
} else {
for (component in nmsStack.componentsPatch.entrySet()) {
nmsComponents[component.key] = component.value.orElse(null)
}
}

val components = mutableMapOf<DataComponentType, Any?>()
for (component in nmsComponents) {
val bukkitType = getBukkitType(component.key) ?: continue
val bukkitValue = component.value?.let { convertNmsValue(bukkitType, it) }
components[bukkitType] = bukkitValue
}
return components
}

override fun hasDefaultComponents(itemStack: ItemStack, components: Set<DataComponentType>): Boolean {
val schema = RebarItemSchema.fromStack(itemStack)
val nmsStack = (itemStack as CraftItemStack).handle
if (schema != null) {
val template = schema.getOriginalTemplate()
val nmsTemplate = (template as CraftItemStack).handle
for (type in components) {
val nmsType = PaperDataComponentType.bukkitToMinecraft<Any>(type)
if (nmsTemplate.get(nmsType) != nmsStack.get(nmsType)) {
return false
}
}
return true
}
return components.none {
val nmsType = PaperDataComponentType.bukkitToMinecraft<Any>(it)
nmsStack.hasNonDefault(nmsType)
}
}

override fun isDefaultComponents(itemStack: ItemStack): Boolean {
val schema = RebarItemSchema.fromStack(itemStack)
val nmsStack = (itemStack as CraftItemStack).handle
if (schema != null) {
val template = schema.getOriginalTemplate()
val nmsTemplate = (template as CraftItemStack).handle
for (type in nmsTemplate.components.keySet()) {
if (nmsTemplate.get(type) != nmsStack.get(type)) {
return false
}
}
return true
}
return nmsStack.componentsPatch.size() == 0
}

override fun componentsMatch(itemStack: ItemStack, components: Map<DataComponentType, Any?>): Boolean {
val nmsStack = (itemStack as CraftItemStack).handle
for (component in components) {
val type = component.key
val value = component.value
val matches = when (type) {
is PaperDataComponentType.NonValuedImpl<*, *> -> componentMatches(nmsStack, type, value != null)
is PaperDataComponentType.ValuedImpl<*, *> -> componentMatches(nmsStack, type, value)
else -> true // should be unreachable
}
if (!matches) {
return false
}
}
return true
}

override fun componentsEqual(itemStack: ItemStack, components: Map<DataComponentType, Any?>): Boolean {
val nmsStack = (itemStack as CraftItemStack).handle
return componentsMatch(itemStack, components) && nmsStack.componentsPatch.size() == components.size
}
}
4 changes: 2 additions & 2 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/Rebar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import io.github.pylonmc.rebar.item.research.Research
import io.github.pylonmc.rebar.logistics.CargoRoutes
import io.github.pylonmc.rebar.metrics.RebarMetrics
import io.github.pylonmc.rebar.recipe.ConfigurableRecipeType
import io.github.pylonmc.rebar.recipe.RebarRecipeListener
import io.github.pylonmc.rebar.recipe.RecipeCompletion
import io.github.pylonmc.rebar.recipe.logic.RebarRecipeListener
import io.github.pylonmc.rebar.recipe.logic.RecipeCompletion
import io.github.pylonmc.rebar.recipe.RecipeType
import io.github.pylonmc.rebar.registry.RebarRegistry
import io.github.pylonmc.rebar.util.delayTicks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ open class ConfigSection private constructor(val name: String?, val internalSect
fun getSectionOrThrow(key: String): ConfigSection =
getSection(key) ?: throw KeyNotFoundException(getKeyPath(key))

/**
* Returns true if the config contains the given [key]
*/
fun has(key: String) = internalSection.contains(key)
Comment thread
JustAHuman-xD marked this conversation as resolved.

/**
* Returns null if the key does not exist or if the value cannot be converted to the desired type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,13 @@ interface ConfigAdapter<T> {

@JvmField val REBAR_FLUID = KEYED.fromRegistry(RebarRegistry.FLUIDS)
@JvmField val FLUID_TEMPERATURE = ENUM.from<FluidTemperature>()

@JvmField val FLUID_CHOICE = FluidChoiceConfigAdapter
@JvmField val ITEM_CHOICE = ItemChoiceConfigAdapter

@JvmField val FLUID_OR_ITEM = FluidOrItemConfigAdapter
@JvmField val RECIPE_INPUT = RecipeInputConfigAdapter
@JvmField val RECIPE_INPUT_ITEM = RecipeInputItemAdapter
@JvmField val RECIPE_INPUT_FLUID = RecipeInputFluidAdapter
@JvmField val RECIPE_CHOICE = RecipeChoiceConfigAdapter
@JvmField val FLUID_WITH_AMOUNT = FluidWithAmountConfigAdapter

@JvmField val ITEM_TAG = ItemTagConfigAdapter
@JvmField val WEIGHTED_SET = WeightedSetConfigAdapter
@JvmField val CULLING_PRESET = CullingPresetConfigAdapter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.recipe.ingredient.FluidChoice
import org.bukkit.configuration.ConfigurationSection

object FluidChoiceConfigAdapter : ConfigAdapter<FluidChoice> {

override val type = FluidChoice::class.java

override fun convert(value: Any): FluidChoice = when (value) {
is Pair<*, *> -> FluidChoice.of(
// e.g. 'pylon:water: 100'
ConfigAdapter.REBAR_FLUID.convert(value.first!!),
ConfigAdapter.DOUBLE.convert(value.second!!)
)
is ConfigurationSection, is Map<*, *> -> {
val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
if (map.size == 1) {
// e.g.
// input:
// pylon:water: 500
return convert(map.entries.first().toPair())
}

// e.g.
// input:
// fluids: [pylon:water, pylon:lava]
// amount: 300
val fluids = ConfigAdapter.SET.from(ConfigAdapter.REBAR_FLUID)
.convert(map["fluids"] ?: error("A list of fluids must be provided e.g. 'fluids: [pylon:water]'"))
val amount = ConfigAdapter.DOUBLE.convert(map["amount"] ?: error("A fluid amount must be specified e.g. 'amount: 500'"))
FluidChoice.of(fluids, amount)
}
else -> throw IllegalArgumentException("Cannot convert $value to FluidChoice")
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.recipe.FluidOrItem
import org.bukkit.configuration.ConfigurationSection
import io.github.pylonmc.rebar.recipe.ingredient.FluidOrItem

object FluidOrItemConfigAdapter : ConfigAdapter<FluidOrItem> {

override val type = FluidOrItem::class.java

override fun convert(value: Any): FluidOrItem {
val item = runCatching { ConfigAdapter.ITEM_STACK.convert(value) }.getOrNull()
return if (item != null) {
FluidOrItem.of(item)
} else when (value) {
is Pair<*, *> -> {
val fluid = ConfigAdapter.REBAR_FLUID.convert(value.first!!)
val amount = ConfigAdapter.DOUBLE.convert(value.second!!)
FluidOrItem.of(fluid, amount)
}

is ConfigurationSection, is Map<*, *> -> convert(MapConfigAdapter.STRING_TO_ANY.convert(value).toList().single())
else -> throw IllegalArgumentException("Cannot convert $value to FluidOrItem")
}
runCatching { ConfigAdapter.ITEM_STACK.convert(value) }.onSuccess { return FluidOrItem.of(it) }
runCatching { FluidWithAmountConfigAdapter.convert(value) }.onSuccess { return it }
throw IllegalArgumentException("Cannot convert $value to FluidOrItem")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.recipe.ingredient.FluidWithAmount
import org.bukkit.configuration.ConfigurationSection

object FluidWithAmountConfigAdapter : ConfigAdapter<FluidWithAmount> {

override val type = FluidWithAmount::class.java

override fun convert(value: Any): FluidWithAmount = when (value) {
is Pair<*, *> -> FluidWithAmount(
ConfigAdapter.REBAR_FLUID.convert(value.first!!),
ConfigAdapter.DOUBLE.convert(value.second!!)
)
is ConfigurationSection, is Map<*, *> -> {
val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
check(map.size == 1) { "The input section had more than one key. Ensure the config is in the format e.g. `pylon:water: 500`" }
convert(map.entries.first().toPair())
}
else -> throw IllegalArgumentException("Cannot convert $value to FluidOrItem.Fluid")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.recipe.ingredient.ItemChoice
import org.bukkit.Registry
import org.bukkit.configuration.ConfigurationSection

object ItemChoiceConfigAdapter : ConfigAdapter<ItemChoice> {

override val type = ItemChoice::class.java

private fun fromString(string: String): ItemChoice.Builder {
val builder = ItemChoice.Builder()
if (string.startsWith("#")) {
// e.g. '#minecraft:acacia_logs'
val tag = ConfigAdapter.ITEM_TAG.convert(string)
for (value in tag.values) {
builder.addFuzzy(value)
}
} else if (string.contains("[")) {
// e.g. 'minecraft:potion[potion_contents={potion:"healing"}]'
val stack = ConfigAdapter.ITEM_STACK.convert(string)
builder.addFuzzy(stack)
} else {
// e.g. 'minecraft:apple'
// e.g. 'pylon:fluid_pipe_copper'
val type = ConfigAdapter.ITEM_TYPE_WRAPPER.convert(string)
builder.addFuzzy(type)
}
return builder
}

private fun fromConfigSection(map: Map<String, Any?>): ItemChoice.Builder {
val item = ConfigAdapter.ITEM_TYPE_WRAPPER.convert(map["item"] ?: error("You must specify an item for recipe inputs"))
val exact = map["exact"]?.let { ConfigAdapter.BOOLEAN.convert(it) } ?: false
val ignore = map["ignore"]?.let {
ConfigAdapter.SET.from(ConfigAdapter.KEYED.fromRegistry(Registry.DATA_COMPONENT_TYPE)).convert(it)
} ?: emptySet()

check(ignore.isEmpty() || exact) {
"You cannot have ignored components unless 'exact' is true"
}

val builder = ItemChoice.Builder()
if (!exact) {
builder.addFuzzy(item)
} else {
builder.addExact(item, ignore)
}

return builder
}

override fun convert(value: Any): ItemChoice = when (value) {
is String -> fromString(value).amount(1).build()

is Pair<*, *> -> {
// e.g. 'pylon:fluid_pipe_bronze: 5'
fromString(value.first!! as String)
.amount(ConfigAdapter.INTEGER.convert(value.second!!))
.build()
}

is ConfigurationSection, is Map<*, *> -> {
// e.g.
// input:
// amount: 5
// choices:
// - item: pylon:bronze_pickaxe
// exact: true
// ignore:
// - max_damage
// - item: pylon:bronze_ingot
// - item: minecraft:stone-pickaxe
// OR
// pylon:bronze_ingot: 5

val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
if (map.size == 1) {
return convert(map.entries.first().toPair())
}

val amount = ConfigAdapter.INTEGER.convert(map["amount"] ?: error("You must specify a recipe amount e.g. 'amount: 5'"))
val choices = ListConfigAdapter.from(ConfigAdapter.ANY).convert(map["choices"] ?: error("You must specify recipe choices e.g. 'choices: [pylon:fluid_pipe_copper, minecraft:apple]'"))

val internalChoices = mutableListOf<ItemChoice.InternalItemChoice>()
for (choice in choices) {
val choiceBuilder = when (choice) {
is String -> fromString(choice)
else -> fromConfigSection(MapConfigAdapter.STRING_TO_ANY.convert(choice))
}
internalChoices.addAll(choiceBuilder.internalChoices)
}
ItemChoice(internalChoices, amount)
}

else -> throw IllegalArgumentException("Cannot convert $value to ItemChoice")
}
}
Loading
Loading