Skip to content

Commit

Permalink
feat: generate resourcepack based on components
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Aug 4, 2024
1 parent 7edb594 commit a0607f9
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class GearyPaperConfig(
@YamlComment("List of mob types to remove if they are not entities with Geary prefabs (i.e. vanilla entities)")
val removeVanillaMobTypes: Set<EntityType> = emptySet(),
val logLevel: Severity = Severity.Info,
val integrations: Integrations = Integrations()
val integrations: Integrations = Integrations(),
val resourcePack: ResourcePack = ResourcePack(),
)

@Serializable
Expand Down Expand Up @@ -71,3 +72,10 @@ data class Integrations(
@YamlComment("Allow binding to MythicMobs entities.")
val mythicMobs: Boolean = true,
)

@Serializable
data class ResourcePack(
val generate: Boolean = true,
@YamlComment("The path to generate the pack to from `plugins/Geary`", "Adding .zip to path will export as a zip instead of directory")
val outputPath: String = "resourcepack.zip"
)
2 changes: 2 additions & 0 deletions geary-papermc-features/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ dependencies {
compileOnly(idofrontLibs.kotlinx.coroutines)
compileOnly(idofrontLibs.minecraft.mccoroutine)
compileOnly(idofrontLibs.idofront.nms)
compileOnly(idofrontLibs.creative.api)
compileOnly(idofrontLibs.creative.serializer.minecraft)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.mineinabyss.geary.papermc.features.common.event_bridge.items.ItemInte
import com.mineinabyss.geary.papermc.features.common.event_bridge.items.ItemRemovedBridge
import com.mineinabyss.geary.papermc.features.common.cooldowns.clearOldCooldownsSystem
import com.mineinabyss.geary.papermc.features.common.cooldowns.cooldownDisplaySystem
import com.mineinabyss.geary.papermc.features.items.resourcepacks.ResourcePackGenerator
import com.mineinabyss.geary.papermc.gearyPaper
import com.mineinabyss.idofront.plugin.listeners

Expand All @@ -32,6 +33,10 @@ open class GearyPaperMCFeatures {
ItemRemovedBridge(),
)
}

geary.pipeline.runOnOrAfter(GearyPhase.INIT_ENTITIES) {
ResourcePackGenerator().generateResourcePack()
}
}

override fun default() = GearyPaperMCFeatures()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.mineinabyss.geary.papermc.features.items.resourcepacks

import com.mineinabyss.idofront.serialization.*
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.kyori.adventure.key.Key
import org.bukkit.Material
import team.unnamed.creative.model.ItemOverride
import team.unnamed.creative.model.ItemPredicate

@Serializable
@SerialName("geary:resourcepack")
data class ResourcePackContent(
val baseMaterial: @Serializable(MaterialByNameSerializer::class) Material,
val model: @Serializable(KeySerializer::class) Key? = null,
val parentModel: @Serializable(KeySerializer::class) Key = Key.key("minecraft:item/generated"),
val textures: @Serializable(ModelTexturesSerializer::class) ModelTexturesSurrogate = ModelTexturesSurrogate(),
val itemPredicates: ItemPredicates
) {

init {
require(model != null || textures.layers.isNotEmpty() || textures.variables.isNotEmpty()) { "ResourcePackContent must contain atleast a model or texture reference" }
}

fun itemOverrides(modelKey: Key): List<ItemOverride> {
val overrides = mutableListOf<ItemOverride>()

// Shields
(itemPredicates.blockingModel ?: itemPredicates.blockingTexture?.let { modelKey.plus("_blocking") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.blocking()))
}
// Elytras
(itemPredicates.brokenModel ?: itemPredicates.brokenTexture?.let { modelKey.plus("_broken") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.broken()))
}
// Fishing Rods
overrides.add(ItemOverride.of(modelKey, itemPredicates.customModelData()))
(itemPredicates.castModel ?: itemPredicates.castTexture?.let { modelKey.plus("_cast") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.cast()))
}
// Charged Crossbow
(itemPredicates.chargedModel ?: itemPredicates.chargedTexture?.let { modelKey.plus("_charged") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.charged()))
}
// Charged Crossbow with Firework
(itemPredicates.fireworkModel ?: itemPredicates.fireworkTexture?.let { modelKey.plus("_firework") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.firework()))
}
// Lefthanded-players
(itemPredicates.lefthandedModel ?: itemPredicates.lefthandedTexture?.let { modelKey.plus("_lefthanded") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.lefthanded()))
}
// Tridents
(itemPredicates.throwingModel ?: itemPredicates.throwingTexture?.let { modelKey.plus("_throwing") })?.let {
overrides.add(ItemOverride.of(it, itemPredicates.customModelData(), ItemPredicate.throwing()))
}

fun Map<Key, Float>.predicateModel(suffix: String, action: (Map.Entry<Key, Float>) -> ItemOverride) = this.toList().mapIndexed { index, (_, damage) -> modelKey.plus("_${suffix}_$index") to damage.coerceIn(0f..1f) }.toMap().map(action).forEach(overrides::add)
// Compasses
itemPredicates.angleModels.takeUnless { it.isEmpty() } ?: itemPredicates.angleTextures.predicateModel("angle") { (key, angle) ->
ItemOverride.of(key, itemPredicates.customModelData(), ItemPredicate.angle(angle))
}
// Cooldown remaining on an item
itemPredicates.cooldownModels.takeUnless { it.isEmpty() } ?: itemPredicates.cooldownTextures.predicateModel("cooldown") { (key, damage) ->
ItemOverride.of(key, itemPredicates.customModelData(), ItemPredicate.damaged(), ItemPredicate.cooldown(damage))
}
// Durability of an item
itemPredicates.damageModels.takeUnless { it.isEmpty() } ?: itemPredicates.damageTextures.predicateModel("damage") { (key, damage) ->
ItemOverride.of(key, itemPredicates.customModelData(), ItemPredicate.damaged(), ItemPredicate.damage(damage))
}
// Bows & Crossbows
itemPredicates.pullingModels.takeUnless { it.isEmpty() } ?: itemPredicates.pullingTextures.predicateModel("pulling") { (key, pulling) ->
ItemOverride.of(key, itemPredicates.customModelData(), ItemPredicate.pulling(), ItemPredicate.pull(pulling))
}
// Clocks
itemPredicates.timeModels.takeUnless { it.isEmpty() } ?: itemPredicates.timeTextures.predicateModel("time") { (key, time) ->
ItemOverride.of(key, itemPredicates.customModelData(), ItemPredicate.time(time))
}

return overrides
}

private fun Key.plus(suffix: String) = Key.key(namespace(), value().plus(suffix))

@Serializable
data class ItemPredicates(
val customModelData: Int,
@EncodeDefault(EncodeDefault.Mode.NEVER) val blockingModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val blockingTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val brokenModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val brokenTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val castModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val castTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val chargedModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val chargedTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val fireworkModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val fireworkTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val lefthandedModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val lefthandedTexture: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val throwingModel: @Serializable(KeySerializer::class) Key? = null,
@EncodeDefault(EncodeDefault.Mode.NEVER) val throwingTexture: @Serializable(KeySerializer::class) Key? = null,

@EncodeDefault(EncodeDefault.Mode.NEVER) val angleModels: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val angleTextures: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val cooldownModels: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val cooldownTextures: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val damageModels: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val damageTextures: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val pullingModels: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val pullingTextures: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val timeModels: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
@EncodeDefault(EncodeDefault.Mode.NEVER) val timeTextures: Map<@Serializable(KeySerializer::class) Key, Float> = emptyMap(),
) {
fun customModelData(): ItemPredicate = ItemPredicate.customModelData(customModelData)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.mineinabyss.geary.papermc.features.items.resourcepacks

import com.mineinabyss.geary.modules.geary
import com.mineinabyss.geary.papermc.gearyPaper
import com.mineinabyss.geary.papermc.tracking.items.components.SetItem
import com.mineinabyss.geary.prefabs.PrefabKey
import com.mineinabyss.geary.prefabs.configuration.components.Prefab
import com.mineinabyss.geary.systems.builders.cache
import com.mineinabyss.geary.systems.query.GearyQuery
import com.mineinabyss.idofront.resourcepacks.ResourcePacks
import net.kyori.adventure.key.Key
import team.unnamed.creative.ResourcePack
import team.unnamed.creative.model.Model
import team.unnamed.creative.model.ModelTexture
import team.unnamed.creative.model.ModelTextures

class ResourcePackGenerator {

private val resourcePack: ResourcePack = ResourcePack.resourcePack()
private val resourcePackQuery = geary.cache(ResourcePackQuery())

fun generateResourcePack() {
if (!gearyPaper.config.resourcePack.generate) return
val resourcePackFile = gearyPaper.plugin.dataFolder.resolve(gearyPaper.config.resourcePack.outputPath)
resourcePackFile.deleteRecursively()

resourcePackQuery.forEach { (prefabKey, resourcePackContent) ->
val defaultVanillaModel = Key.key("item/${resourcePackContent.baseMaterial.key().value()}").let { resourcePack.model(it)?.toBuilder() ?: Model.model().key(it) }

// Generates any missing models for predicates if only textures are provided
generatePredicateModels(resourcePack, resourcePackContent, prefabKey)

// If a model is defined we assume it exists in the resourcepack already, and just add the override to the vanilla model
if (resourcePackContent.model != null) {
resourcePackContent.itemOverrides(resourcePackContent.model.key()).forEach(defaultVanillaModel::addOverride)
} else { // If it only has textures we need to generate the model ourselves and add it
val model = Model.model()
.key(Key.key(prefabKey.namespace, prefabKey.key))
.parent(resourcePackContent.parentModel.key())
.textures(resourcePackContent.textures.modelTextures).build()
resourcePackContent.itemOverrides(model.key()).forEach(defaultVanillaModel::addOverride)
model.addTo(resourcePack)
}

defaultVanillaModel.build().addTo(resourcePack)
}

when {
resourcePackFile.extension == "zip" -> ResourcePacks.resourcePackWriter.writeToZipFile(resourcePackFile, resourcePack)
resourcePackFile.isDirectory -> ResourcePacks.resourcePackWriter.writeToDirectory(resourcePackFile, resourcePack)
else -> {
gearyPaper.logger.w("Failed to generate resourcepack in ${resourcePackFile.path}")
gearyPaper.logger.w("Outputting to default plugins/Geary/resourcepack directory")
ResourcePacks.resourcePackWriter.writeToDirectory(gearyPaper.plugin.dataFolder.resolve("resourcepack"), resourcePack)
}
}
}

private fun generatePredicateModels(resourcePack: ResourcePack, resourcePackContent: ResourcePackContent, prefabKey: PrefabKey) {
fun predicateModel(modelKey: Key, suffix: String) {
Model.model().key(Key.key(prefabKey.namespace, prefabKey.key.plus(suffix)))
.parent(resourcePackContent.parentModel)
.textures(ModelTextures.of(listOf(ModelTexture.ofKey(modelKey)), null, emptyMap()))
.build().addTo(resourcePack)
}
resourcePackContent.itemPredicates.blockingTexture?.let { predicateModel(it, "_blocking") }
resourcePackContent.itemPredicates.brokenTexture?.let { predicateModel(it, "_broken") }
resourcePackContent.itemPredicates.castTexture?.let { predicateModel(it, "_cast") }
resourcePackContent.itemPredicates.chargedTexture?.let { predicateModel(it, "_charged") }
resourcePackContent.itemPredicates.fireworkTexture?.let { predicateModel(it, "_firework") }
resourcePackContent.itemPredicates.lefthandedTexture?.let { predicateModel(it, "_lefthanded") }
resourcePackContent.itemPredicates.throwingTexture?.let { predicateModel(it, "_throwing") }
resourcePackContent.itemPredicates.angleTextures.onEachIndexed { i, (key, _) -> predicateModel(key, "_angle_$i") }
resourcePackContent.itemPredicates.cooldownTextures.onEachIndexed { i, (key, _) -> predicateModel(key, "_cooldown_$i") }
resourcePackContent.itemPredicates.damageTextures.onEachIndexed { i, (key, _) -> predicateModel(key, "_damage_$i") }
resourcePackContent.itemPredicates.pullingTextures.onEachIndexed { i, (key, _) -> predicateModel(key, "_pulling_$i") }
resourcePackContent.itemPredicates.timeTextures.onEachIndexed { i, (key, _) -> predicateModel(key, "_time_$i") }

}

companion object {
class ResourcePackQuery : GearyQuery() {
private val prefabKey by get<PrefabKey>()
private val resourcePackContent by get<ResourcePackContent>()

override fun ensure() = this {
has<Prefab>()
}

operator fun component1() = prefabKey
operator fun component2() = resourcePackContent
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mineinabyss.geary.papermc.plugin.commands

import com.mineinabyss.geary.papermc.features.items.resourcepacks.ResourcePackGenerator
import com.mineinabyss.geary.papermc.gearyPaper
import com.mineinabyss.geary.prefabs.prefabs
import com.mineinabyss.idofront.commands.Command
Expand All @@ -11,6 +12,7 @@ fun Command.reload() {
action {
gearyPaper.configHolder.reload()
prefabLoader.loadOrUpdatePrefabs()
ResourcePackGenerator().generateResourcePack()
}
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kotlin.code.style=official
group=com.mineinabyss
version=0.30
idofrontVersion=0.24.14
idofrontVersion=0.24.17

0 comments on commit a0607f9

Please sign in to comment.