Skip to content

Commit 916ba62

Browse files
committed
feat: idofront-services module, used to let other plugins register 'item providers' for serializable itemstack
chore: Remove extra plugin dependencies used for SerializableItemStack, move all others to mirror repo
1 parent dc826ef commit 916ba62

15 files changed

Lines changed: 151 additions & 122 deletions

File tree

.editorconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[*.kt]
2+
max_line_length = off

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Changelog
2+
3+
All notable changes to this project between Minecraft versions will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). We currently do not follow semver but
6+
try to keep MAJOR changes in sync with Minecraft updates.
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- New `idofront-services` module for interfaces implemented by this plugin using Bukkit's ServiceManager API
13+
- Start a changelog based on Keep a Changelog
14+
- `Services.register` helper function
15+
16+
### Changed
17+
18+
- SerializableItemStack uses a new service for letting other plugins register custom item types instead of manually
19+
adding support for them
20+
21+
## [1.0.0] - 2025-08-12
22+
23+
### Changed
24+
25+
- Update to Minecraft 1.21.8
26+
- Update to Kotlin 2.2
27+
- Add androidx.sqlite library which provides lightweight native bindings we plan to use for our own database library

build.gradle.kts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,9 @@ subprojects {
1717
mavenCentral()
1818
maven("https://repo.mineinabyss.com/releases")
1919
maven("https://repo.mineinabyss.com/snapshots")
20-
mavenLocal()
20+
maven("https://repo.mineinabyss.com/mirror")
2121
maven("https://repo.papermc.io/repository/maven-public/")
22-
maven("https://repo.spaceio.xyz/repository/maven-snapshots/")
23-
maven("https://jitpack.io")
24-
maven("https://mvn.lumine.io/repository/maven-public/") { metadataSources { artifact() } }// MythicMobs
25-
maven("https://repo.nexomc.com/releases")
26-
maven("https://repo.nexomc.com/snapshots")
22+
mavenLocal()
2723
}
2824

2925
tasks {

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group=com.mineinabyss
2-
version=1.0
2+
version=2.0
33
# Version to use for our conventions plugins.
44
# Idofront will point to this version in its catalog.
55
conventionsVersion=1.0.3

idofront-catalog-shaded/src/main/java/com/mineinabyss/idofront/IdofrontPlugin.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
package com.mineinabyss.idofront
22

33
import com.mineinabyss.idofront.di.DI
4+
import com.mineinabyss.idofront.plugin.Services
45
import com.mineinabyss.idofront.plugin.listeners
56
import com.mineinabyss.idofront.serialization.recipes.options.IngredientOptionsListener
7+
import com.mineinabyss.idofront.services.SerializableItemStackService
8+
import com.mineinabyss.idofront.services.impl.SerializableItemStackServiceImpl
69
import org.bukkit.plugin.java.JavaPlugin
710

811
class IdofrontPlugin : JavaPlugin() {
12+
override fun onLoad() {
13+
Services.register<SerializableItemStackService>(this, SerializableItemStackServiceImpl())
14+
}
15+
916
override fun onEnable() {
1017
val recipeOptionsListener = IngredientOptionsListener(this)
1118
DI.add(recipeOptionsListener)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.mineinabyss.idofront.services.impl
2+
3+
import com.mineinabyss.idofront.messaging.idofrontLogger
4+
import com.mineinabyss.idofront.services.ItemProvider
5+
import com.mineinabyss.idofront.services.SerializableItemStackService
6+
7+
class SerializableItemStackServiceImpl : SerializableItemStackService {
8+
val extensions = mutableMapOf<String, ItemProvider>()
9+
10+
override fun registerProvider(prefix: String, provider: ItemProvider) {
11+
if (prefix.contains(" ")) {
12+
error("Cannot register item provider with space in prefix: $prefix")
13+
}
14+
15+
// Print warning with strack trace to know which plugin is causing this issue.
16+
if (extensions.containsKey(prefix)) {
17+
idofrontLogger.w { IllegalStateException("An item provider with prefix '$prefix' was already registered").stackTraceToString() }
18+
} else {
19+
extensions[prefix] = provider
20+
}
21+
}
22+
23+
override fun getProvider(prefix: String): ItemProvider? {
24+
return extensions[prefix]
25+
}
26+
}

idofront-serializers/build.gradle.kts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ dependencies {
99
compileOnly(libs.kotlin.reflect)
1010
compileOnly(libs.kotlinx.serialization.json)
1111
compileOnly(libs.kotlinx.serialization.kaml)
12-
compileOnly(libs.minecraft.plugin.mythic.dist)
13-
compileOnly(libs.minecraft.plugin.mythic.crucible)
14-
compileOnly(libs.minecraft.plugin.nexo)
15-
compileOnly(libs.minecraft.plugin.itemsadder)
1612
compileOnly(libs.creative.api) { isTransitive = true }
1713
implementation(projects.idofrontUtil)
1814
implementation(projects.idofrontLogging)
1915
implementation(projects.idofrontTextComponents)
2016
implementation(projects.idofrontDi)
17+
implementation(projects.idofrontServices)
2118
}

idofront-serializers/src/main/kotlin/com/mineinabyss/idofront/serialization/MaterialByNameSerializer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,4 @@ object MaterialByNameSerializer : KSerializer<Material> {
2020
val name = decoder.decodeString()
2121
return Material.matchMaterial(name) ?: error("Material $name does not exist")
2222
}
23-
2423
}

idofront-serializers/src/main/kotlin/com/mineinabyss/idofront/serialization/SerializableItemStack.kt

Lines changed: 32 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,23 @@
22

33
package com.mineinabyss.idofront.serialization
44

5-
import com.mineinabyss.idofront.di.DI
65
import com.mineinabyss.idofront.messaging.idofrontLogger
7-
import com.mineinabyss.idofront.plugin.Plugins
6+
import com.mineinabyss.idofront.plugin.Services
87
import com.mineinabyss.idofront.serialization.recipes.options.IngredientOption
8+
import com.mineinabyss.idofront.services.SerializableItemStackService
99
import com.mineinabyss.idofront.textcomponents.miniMsg
1010
import com.mineinabyss.idofront.textcomponents.serialize
11-
import com.nexomc.nexo.NexoPlugin
12-
import com.nexomc.nexo.api.NexoItems
13-
import dev.lone.itemsadder.api.CustomStack
14-
import io.lumine.mythiccrucible.MythicCrucible
1511
import io.papermc.paper.datacomponent.DataComponentType
1612
import io.papermc.paper.datacomponent.DataComponentTypes
1713
import io.papermc.paper.datacomponent.item.ItemLore
1814
import io.papermc.paper.datacomponent.item.MapDecorations
1915
import io.papermc.paper.item.MapPostProcessing
2016
import io.papermc.paper.registry.RegistryAccess
2117
import io.papermc.paper.registry.RegistryKey
22-
import kotlinx.serialization.Contextual
2318
import kotlinx.serialization.*
2419
import kotlinx.serialization.EncodeDefault.Mode.NEVER
2520
import net.kyori.adventure.key.Key
26-
import net.kyori.adventure.text.Component
27-
import net.kyori.adventure.text.format.TextDecoration
28-
import org.bukkit.Art
29-
import org.bukkit.Bukkit
30-
import org.bukkit.Keyed
31-
import org.bukkit.Material
32-
import org.bukkit.NamespacedKey
33-
import org.bukkit.Registry
21+
import org.bukkit.*
3422
import org.bukkit.inventory.ItemRarity
3523
import org.bukkit.inventory.ItemStack
3624
import org.bukkit.inventory.RecipeChoice
@@ -40,11 +28,14 @@ typealias SerializableItemStack = @Serializable(with = SerializableItemStackSeri
4028
/**
4129
* A wrapper for [ItemStack] that uses [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization).
4230
* Allows for easy-to-use serialization to JSON (or YAML with kaml).
31+
*
32+
* @param type The item type to inherit a baseline configuration from, other plugins may register extra providers for this.
4333
*/
4434
@Suppress("UnstableApiUsage")
4535
@Serializable
4636
data class BaseSerializableItemStack(
47-
@EncodeDefault(NEVER) val type: @Serializable(with = MaterialByNameSerializer::class) Material? = null,
37+
@EncodeDefault(NEVER) val material: @Serializable(with = MaterialByNameSerializer::class) Material? = null,
38+
@EncodeDefault(NEVER) val type: String? = null,
4839
@EncodeDefault(NEVER) val amount: Int? = null,
4940
@EncodeDefault(NEVER) val customModelData: SerializableDataTypes.CustomModelData? = null,
5041
@EncodeDefault(NEVER) val itemModel: @Serializable(KeySerializer::class) Key? = null,
@@ -103,75 +94,46 @@ data class BaseSerializableItemStack(
10394
@EncodeDefault(NEVER) val noteBlockSound: @Serializable(KeySerializer::class) Key? = null,
10495
@EncodeDefault(NEVER) val potDecorations: SerializableDataTypes.PotDecorations? = null,
10596

106-
107-
@EncodeDefault(NEVER) val prefab: String? = null,
10897
@EncodeDefault(NEVER) val tag: @Serializable(NamespacedKeySerializer::class) NamespacedKey? = null,
10998
@EncodeDefault(NEVER) val recipeOptions: List<IngredientOption> = listOf(),
11099

111100
// Unvalued DataTypes
112101
@EncodeDefault(NEVER) @Contextual val intangibleProjectile: SerializableDataTypes.IntangibleProjectile? = null,
113102
@EncodeDefault(NEVER) @Contextual val glider: SerializableDataTypes.Glider? = null,
114103
@EncodeDefault(NEVER) val unbreakable: SerializableDataTypes.Unbreakable? = null,
115-
116-
// Third-party plugins
117-
@EncodeDefault(NEVER) val crucibleItem: String? = null,
118-
@EncodeDefault(NEVER) val nexoItem: String? = null,
119-
@EncodeDefault(NEVER) val itemsadderItem: String? = null,
120104
) {
121-
private fun Component.removeItalics() =
122-
Component.text().decoration(TextDecoration.ITALIC, false).build().append(this)
123-
124-
@Transient val itemName = _itemName?.miniMsg()
125-
@Transient val customName = _customName?.miniMsg()
126-
@Transient val lore = _lore?.map { it.miniMsg() }
105+
@Transient
106+
val itemName = _itemName?.miniMsg()
107+
108+
@Transient
109+
val customName = _customName?.miniMsg()
110+
111+
@Transient
112+
val lore = _lore?.map { it.miniMsg() }
113+
114+
@Transient
115+
val itemProvider = type?.let {
116+
val provider = Services
117+
.getOrNull<SerializableItemStackService>()
118+
?.getProvider(it.substringBefore(' ', missingDelimiterValue = ""))
119+
if (provider == null) idofrontLogger.w { "Item provider for '$type' could not be found." }
120+
provider
121+
}
127122

128123
/**
129124
* Converts this serialized item's data to an [ItemStack], optionally applying the changes to an
130125
* [existing item][applyTo].
131126
*/
132-
fun toItemStack(applyTo: ItemStack = ItemStack.of(type ?: Material.AIR)): ItemStack {
133-
// Import ItemStack from Crucible
134-
crucibleItem?.let { id ->
135-
if (Plugins.isEnabled<MythicCrucible>()) {
136-
MythicCrucible.core().itemManager.getItemStack(id)?.let {
137-
applyTo.type = it.type
138-
applyTo.itemMeta = it.itemMeta
139-
} ?: idofrontLogger.w("No Crucible item found with id $id")
140-
} else {
141-
idofrontLogger.w("Tried to import Crucible item, but MythicCrucible was not enabled")
142-
}
143-
}
144-
145-
// Import ItemStack from Nexo
146-
nexoItem?.let { id ->
147-
if (Plugins.isEnabled<NexoPlugin>()) {
148-
NexoItems.itemFromId(id)?.build()?.let {
149-
applyTo.type = it.type
150-
applyTo.itemMeta = it.itemMeta
151-
} ?: idofrontLogger.w("No Nexo item found with id $id")
152-
} else {
153-
idofrontLogger.w("Tried to import Nexo item, but Nexo was not enabled")
154-
}
127+
fun toItemStack(applyTo: ItemStack = ItemStack.of(material ?: Material.AIR)): ItemStack {
128+
if (type != null && itemProvider != null) {
129+
val definition = type.substringAfter(' ')
130+
val success = itemProvider.invoke(applyTo, definition)
131+
if (!success) idofrontLogger.w { "Item provider for '$type' could not find such item." }
155132
}
156133

157-
// Import ItemStack from ItemsAdder
158-
itemsadderItem?.let { id ->
159-
if (Plugins.isEnabled("ItemsAdder")) {
160-
CustomStack.getInstance(id)?.itemStack?.let {
161-
applyTo.type = it.type
162-
applyTo.itemMeta = it.itemMeta
163-
} ?: idofrontLogger.w("No ItemsAdder item found with id $id")
164-
} else {
165-
idofrontLogger.w("Tried to import ItemsAdder item, but ItemsAdder was not enabled")
166-
}
167-
}
168-
169-
// Support for our prefab system in geary.
170-
prefab?.let { encodePrefab.invoke(applyTo, it) } ?: applyTo
171-
172134
// Modify item
173135
amount?.let { applyTo.amount = it }
174-
type?.let { applyTo.type = type }
136+
material?.let { applyTo.type = material }
175137

176138
SerializableDataTypes.setData(applyTo, DataComponentTypes.ITEM_NAME, itemName)
177139
SerializableDataTypes.setData(applyTo, DataComponentTypes.CUSTOM_NAME, customName)
@@ -237,7 +199,7 @@ data class BaseSerializableItemStack(
237199
return applyTo
238200
}
239201

240-
fun toItemStackOrNull(applyTo: ItemStack = ItemStack(type ?: Material.AIR)) =
202+
fun toItemStackOrNull(applyTo: ItemStack = ItemStack(material ?: Material.AIR)) =
241203
toItemStack(applyTo).takeUnless { it.isEmpty }
242204

243205
fun toRecipeChoice(): RecipeChoice = toItemStackOrNull()?.let(RecipeChoice::ExactChoice) ?: RecipeChoice.empty()
@@ -246,11 +208,6 @@ data class BaseSerializableItemStack(
246208
fun matches(item: ItemStack): Boolean {
247209
return item == toItemStack(applyTo = item.clone())
248210
}
249-
250-
companion object {
251-
@Suppress("UNCHECKED_CAST")
252-
private val encodePrefab by DI.observe<SerializablePrefabItemService>()
253-
}
254211
}
255212

256213
/**
@@ -260,7 +217,7 @@ data class BaseSerializableItemStack(
260217
*/
261218
fun ItemStack.toSerializable(): SerializableItemStack = with(itemMeta) {
262219
SerializableItemStack(
263-
type = type,
220+
material = type,
264221
amount = amount.takeIf { it != 1 },
265222
_itemName = dataIfOverriden(DataComponentTypes.ITEM_NAME)?.serialize(),
266223
_customName = dataIfOverriden(DataComponentTypes.CUSTOM_NAME)?.serialize(),
@@ -319,7 +276,6 @@ fun ItemStack.toSerializable(): SerializableItemStack = with(itemMeta) {
319276

320277
intangibleProjectile = SerializableDataTypes.IntangibleProjectile.takeIf { hasData(DataComponentTypes.INTANGIBLE_PROJECTILE) },
321278
unbreakable = SerializableDataTypes.Unbreakable.takeIf { hasData(DataComponentTypes.UNBREAKABLE) },
322-
323279
)
324280
}
325281

idofront-serializers/src/main/kotlin/com/mineinabyss/idofront/serialization/SerializableItemStackSerializer.kt

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
package com.mineinabyss.idofront.serialization
22

33
import com.charleskorn.kaml.YamlInput
4-
import com.charleskorn.kaml.YamlList
54
import com.charleskorn.kaml.YamlScalar
65
import kotlinx.serialization.ContextualSerializer
7-
import kotlinx.serialization.InternalSerializationApi
86
import kotlinx.serialization.KSerializer
9-
import kotlinx.serialization.descriptors.SerialDescriptor
107
import kotlinx.serialization.encoding.Decoder
118
import kotlinx.serialization.encoding.Encoder
12-
import kotlinx.serialization.encoding.decodeStructure
139
import org.bukkit.Material
1410

1511
class SerializableItemStackSerializer : KSerializer<SerializableItemStack> {
16-
// override val descriptor: SerialDescriptor = BaseSerializableItemStack.serializer().descriptor
17-
override val descriptor: SerialDescriptor =
18-
ContextualSerializer(BaseSerializableItemStack::class).descriptor
12+
override val descriptor = ContextualSerializer(BaseSerializableItemStack::class).descriptor
1913

2014
override fun deserialize(decoder: Decoder): SerializableItemStack {
2115
val node = (decoder as? YamlInput)?.node ?: return BaseSerializableItemStack.serializer().deserialize(decoder)
@@ -34,20 +28,12 @@ class SerializableItemStackSerializer : KSerializer<SerializableItemStack> {
3428

3529
fun decodeFromShorthand(stringShorthand: String) = when {
3630
stringShorthand.startsWith("minecraft:") -> BaseSerializableItemStack(
37-
type = Material.matchMaterial(
38-
stringShorthand
39-
)
31+
material = Material.matchMaterial(stringShorthand)
4032
)
4133

42-
stringShorthand.startsWith("crucible ") -> BaseSerializableItemStack(
43-
crucibleItem = stringShorthand.removePrefix("crucible ")
44-
)
45-
46-
stringShorthand.startsWith("nexo ") -> BaseSerializableItemStack(
47-
nexoItem = stringShorthand.removePrefix("nexo ")
48-
)
49-
50-
else -> BaseSerializableItemStack(prefab = stringShorthand)
34+
else -> {
35+
BaseSerializableItemStack(type = stringShorthand)
36+
}
5137
}
5238

5339
override fun serialize(encoder: Encoder, value: SerializableItemStack) {

0 commit comments

Comments
 (0)