Skip to content

Commit

Permalink
feat: obfuscate sounds & improved pack-obfuscation
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Jul 22, 2024
1 parent 00668ae commit 53a4a3d
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 3,778 deletions.
199 changes: 132 additions & 67 deletions src/main/kotlin/com/mineinabyss/packy/PackObfuscator.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.mineinabyss.packy

import com.mineinabyss.idofront.messaging.broadcastVal
import com.mineinabyss.idofront.messaging.logVal
import com.mineinabyss.packy.config.PackyConfig
import com.mineinabyss.packy.config.packy
import com.mineinabyss.packy.helpers.VanillaPackKeys
import com.mineinabyss.packy.helpers.VanillaKeys
import net.kyori.adventure.key.Key
import team.unnamed.creative.ResourcePack
import team.unnamed.creative.atlas.AtlasSource
import team.unnamed.creative.atlas.SingleAtlasSource
import team.unnamed.creative.blockstate.BlockState
import team.unnamed.creative.blockstate.MultiVariant
import team.unnamed.creative.blockstate.Selector
import team.unnamed.creative.blockstate.Variant
Expand All @@ -13,70 +18,83 @@ import team.unnamed.creative.model.ItemOverride
import team.unnamed.creative.model.Model
import team.unnamed.creative.model.ModelTexture
import team.unnamed.creative.model.ModelTextures
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter
import team.unnamed.creative.sound.Sound
import team.unnamed.creative.sound.SoundRegistry
import team.unnamed.creative.texture.Texture
import java.util.*

object PackObfuscator {
class PackObfuscator(private val resourcePack: ResourcePack) {

private lateinit var resourcePack: ResourcePack
private class ObfuscatedModel(val originalModel: Model, val obfuscatedModel: Model) {
fun find(key: Key) = originalModel.takeIf { it.key() == key } ?: obfuscatedModel.takeIf { it.key() == key }
}

private class ObfuscatedModel(val originalModel: Model, val obfuscatedModel: Model)
private class ObfuscatedTexture(
val originalTexture: Texture,
val obfuscatedTexture: Texture,
val isItemTexture: Boolean = false
)
val obfuscatedTexture: Texture
) {
fun find(key: Key) = originalTexture.takeIf { it.key() == key } ?: obfuscatedTexture.takeIf { it.key() == key }
}

private class ObfuscatedSound(val originalSound: Sound, val obfuscatedSound: Sound) {
fun find(key: Key) = originalSound.takeIf { it.key() == key } ?: obfuscatedSound.takeIf { it.key() == key }
}

private val obfuscatedModels = mutableSetOf<ObfuscatedModel>()
private val obfuscatedTextures = mutableSetOf<ObfuscatedTexture>()
private val obfuscatedSounds = mutableSetOf<ObfuscatedSound>()

fun obfuscatePack(resourcePack: ResourcePack) {
private fun Set<ObfuscatedModel>.findObf(key: Key) = firstOrNull { it.find(key) != null }?.obfuscatedModel
private fun Set<ObfuscatedTexture>.findObf(key: Key) = firstOrNull { it.find(key) != null }?.obfuscatedTexture
private fun Set<ObfuscatedSound>.findObf(key: Key) = firstOrNull { it.find(key) != null }?.obfuscatedSound

fun obfuscatePack() {
if (packy.config.obfuscation == PackyConfig.ObfuscationType.NONE) return
packy.logger.i("Obfuscating pack...")
PackObfuscator.resourcePack = resourcePack

obfuscatedModels.clear()
obfuscatedTextures.clear()

obfuscateModels()
obfuscateFonts()
obfuscateTextures()
MinecraftResourcePackWriter.minecraft().writeToZipFile(packy.plugin.dataFolder.resolve("obfuscated.zip"), resourcePack)
obfuscateSounds()

packy.logger.s("Finished obfuscating pack!")
}

private fun obfuscateModels() {
resourcePack.models().mapNotNull { it }.forEach { obfuscateModel(it) }
resourcePack.models().filterNotNull().forEach { obfuscateModel(it) }

obfuscatedModels.forEach {
resourcePack.removeModel(it.originalModel.key())
it.obfuscatedModel.addTo(resourcePack)
}

obfuscatedModels.map { it.originalModel.key() }.forEach(resourcePack::removeModel)
obfuscatedModels.map { it.obfuscatedModel }.toSet().forEach(resourcePack::model)
obfuscateBlockStates()
}

private fun obfuscateModel(model: Model): Model {
return when {
model.key() in VanillaPackKeys.defaultModels -> model.obfuscateOverrides()
else -> model.obfuscateModelTextures().obfuscate()
}
}
private fun obfuscateBlockStates() {
resourcePack.blockStates().filterNotNull().forEach { blockState ->
val multiparts = blockState.multipart().map {
Selector.of(it.condition(), MultiVariant.of(it.variant().variants().map { v -> v.obfuscateVariant() }))
}

private fun Model.obfuscateModelTextures(): Model {
val layers = textures().layers().filterNotNull().filter { it.key() != null }.map { modelTexture ->
obfuscateItemTexture(modelTexture)?.keyNoPng?.let { ModelTexture.ofKey(it) } ?: modelTexture
}
val variables = textures().variables().map { variable ->
variable.key to (obfuscateItemTexture(variable.value)?.keyNoPng?.let { ModelTexture.ofKey(it) } ?: variable.value)
}.toMap()
val variants = blockState.variants().map {
it.key to MultiVariant.of(it.value.variants().map { v -> v.obfuscateVariant() })
}.toMap()

val particle = textures().particle()?.let { p -> obfuscateItemTexture(p)?.keyNoPng?.let { ModelTexture.ofKey(it) } ?: p }
val modelTextures = ModelTextures.builder().layers(layers).variables(variables).particle(particle).build()
return this.toBuilder().textures(modelTextures).build()
BlockState.of(blockState.key(), variants, multiparts).addTo(resourcePack)
}
}

private fun obfuscateFonts() {
resourcePack.fonts().mapNotNull { it }.map { font ->
font.providers(font.providers().filterNotNull().map { provider ->
when (provider) {
is BitMapFontProvider -> provider.toBuilder().file(obfuscateFontTexture(provider)?.key() ?: provider.file()).build()
is BitMapFontProvider -> provider.toBuilder()
.file(obfuscateFontTexture(provider)?.key() ?: provider.file()).build()

else -> provider
}
})
Expand All @@ -89,60 +107,107 @@ object PackObfuscator {
resourcePack.texture(it.obfuscatedTexture)
}

// Obfuscate the atlas sources
resourcePack.atlases().filterNotNull().map { atlas ->
atlas.toBuilder().sources(obfuscatedTextures.filter { it.isItemTexture }.map { it.obfuscatedTexture.keyNoPng }.map(AtlasSource::single)).build()
}.forEach(resourcePack::atlas)
obfuscateAtlases()
}

private fun obfuscateBlockStates() {
resourcePack.blockStates().filterNotNull().forEach { blockState ->
val multiparts = blockState.multipart().map {
Selector.of(it.condition(), MultiVariant.of(it.variant().variants().map { v -> v.obfuscateVariant() }))
private fun obfuscateAtlases() {
resourcePack.atlases().map { atlas ->
val nonSingleSources = atlas.sources().filterNot { it is SingleAtlasSource }.toMutableList()
val obfSources = atlas.sources().filterIsInstance<SingleAtlasSource>().map { s ->
obfuscatedTextures.firstOrNull { it.find(s.resource()) != null }?.let {
AtlasSource.single(it.obfuscatedTexture.key().removeSuffix(".png"))
} ?: s
}
blockState.multipart().clear()
blockState.multipart().addAll(multiparts)

val variants = blockState.variants().map {
it.key to MultiVariant.of(it.value.variants().map { v -> v.obfuscateVariant() })
}
blockState.variants().clear()
blockState.variants().putAll(variants)
resourcePack.blockState(blockState)
nonSingleSources += obfSources
atlas.toBuilder().sources(nonSingleSources).build()
}.forEach(resourcePack::atlas)
}

private fun obfuscateSounds() {
resourcePack.sounds().map { sound ->
val obfSound = Sound.sound(sound.key().obfuscateKey(), sound.data())
obfuscatedSounds += ObfuscatedSound(sound, obfSound)
return@map obfSound
}.toList().forEach(resourcePack::sound)

resourcePack.soundRegistries().map soundRegistries@{ soundRegistry ->
SoundRegistry.soundRegistry(
soundRegistry.namespace(),
soundRegistry.sounds().map soundEvents@{ soundEvent ->
soundEvent.toBuilder().sounds(soundEvent.sounds().map soundEntries@{ soundEntry ->
obfuscatedSounds.findObf(soundEntry.key())?.let {
soundEntry.toBuilder().key(it.key()).build()
} ?: soundEntry
}).build()
}).addTo(resourcePack)
}

obfuscatedSounds.forEach {
resourcePack.removeSound(it.originalSound.key())
it.obfuscatedSound.addTo(resourcePack)
}
}


private fun obfuscateModel(model: Model) =
obfuscatedModels.findObf(model.key()) ?: model.obfuscateModelTextures().obfuscateOverrides()

private fun Model.obfuscateModelTextures(): Model {
obfuscatedModels.findObf(this.key())?.let { return it }
val layers = textures().layers().filter { it.key() != null }.map { modelTexture ->
obfuscateItemTexture(modelTexture)?.key()?.let { ModelTexture.ofKey(it) } ?: modelTexture
}
val variables = textures().variables().map { variable ->
variable.key to (obfuscateItemTexture(variable.value)?.key()
?.let(ModelTexture::ofKey) ?: variable.value)
}.toMap()

val particle = textures().particle()
?.let { p -> obfuscateItemTexture(p)?.key()?.let { ModelTexture.ofKey(it) } ?: p }
val modelTextures = ModelTextures.builder().layers(layers).variables(variables).particle(particle).build()
return this.toBuilder().textures(modelTextures).build()
}

private fun Variant.obfuscateVariant(): Variant {
return Variant.builder()
.model(obfuscatedModels.find { it.originalModel.key() == model() }?.obfuscatedModel?.key() ?: model())
.model(obfuscatedModels.findObf(model())?.key() ?: model())
.uvLock(uvLock()).weight(weight()).x(x()).y(y()).build()
}

private val Texture.keyNoPng get() = key().removeSuffix(".png")
private fun Key.removeSuffix(suffix: String) = Key.key(asString().removeSuffix(suffix))
private fun Model.obfuscateOverrides(): Model = obfuscatedModels.findObf(key()) ?: toBuilder().overrides(
overrides().filterNotNull().map { override ->
if (VanillaKeys.isVanilla(override.model())) return@map override
val modelKey = obfuscatedModels.findObf(override.model())?.key()
?: resourcePack.takeUnless { override.model() == this.key() }?.model(override.model())?.let { obfuscateModel(it) }?.key()
?: override.model()

private fun Model.obfuscate() = this.obfuscateOverrides().toBuilder().key(Key.key(UUID.randomUUID().toString())).build()
.apply { obfuscatedModels += ObfuscatedModel(this@obfuscate, this) }
private fun Model.obfuscateOverrides() = toBuilder().overrides(overrides().filterNotNull().map { override ->
if (override.model() in VanillaPackKeys.defaultModels) return@map override
ItemOverride.of((resourcePack.models().find { it.key() == override.model() }?.let { obfuscateModel(it) }
?: obfuscatedModels.find { it.originalModel.key() == override.model() || it.obfuscatedModel.key() == override.model() }?.obfuscatedModel
?: override.model()).key(), override.predicate())
}).build().apply { obfuscatedModels += ObfuscatedModel(this@obfuscateOverrides, this) }
return@map ItemOverride.of(modelKey, override.predicate())
}
).key(key().takeUnless(VanillaKeys::isVanilla)?.obfuscateKey() ?: key()).build()
.also { obfuscatedModels += ObfuscatedModel(this@obfuscateOverrides, it) }

private fun Texture.obfuscate(isItemTexture: Boolean) =
this.toBuilder().key(Key.key(UUID.randomUUID().toString() + ".png")).build()
.apply { obfuscatedTextures += ObfuscatedTexture(this@obfuscate, this, isItemTexture) }

private fun Key.removeSuffix(suffix: String) = Key.key(asString().removeSuffix(suffix))
private fun Key.appendSuffix(suffix: String) = Key.key(asString().removeSuffix(suffix).plus(suffix))

private fun Texture.obfuscate() =
this.toBuilder().key(this.key().obfuscateKey()).build()
.also { obfuscatedTextures += ObfuscatedTexture(this@obfuscate, it) }

private fun obfuscateItemTexture(modelTexture: ModelTexture): Texture? {
val keyPng = modelTexture.key()?.let { Key.key(it.removeSuffix(".png").asString() + ".png") }
return obfuscatedTextures.find { it.originalTexture.key() == keyPng }?.obfuscatedTexture
?: resourcePack.textures().find { it.key() == keyPng }?.obfuscate(true)
val keyPng = modelTexture.key()?.removeSuffix(".png") ?: return null
return obfuscatedTextures.findObf(keyPng) ?: resourcePack.texture(keyPng)?.obfuscate()
}

private fun obfuscateFontTexture(provider: BitMapFontProvider): Texture? {
val keyPng = Key.key(provider.file().removeSuffix(".png").asString() + ".png")
return obfuscatedTextures.find { it.originalTexture.key() == keyPng }?.obfuscatedTexture
?: resourcePack.textures().find { it.key() == keyPng }?.obfuscate(false)
val keyPng = provider.file().appendSuffix(".png")
return obfuscatedTextures.findObf(keyPng) ?: resourcePack.texture(keyPng)?.obfuscate()
}

private fun Key.obfuscateKey() = when (packy.config.obfuscation) {
PackyConfig.ObfuscationType.NONE -> this
PackyConfig.ObfuscationType.FULL -> Key.key(UUID.randomUUID().toString(), UUID.randomUUID().toString())
PackyConfig.ObfuscationType.SIMPLE -> Key.key(this.namespace(), UUID.randomUUID().toString())
}.let { if (asString().endsWith(".png")) it.appendSuffix(".png") else it.removeSuffix(".png") }
}
18 changes: 9 additions & 9 deletions src/main/kotlin/com/mineinabyss/packy/PackyGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ package com.mineinabyss.packy

import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher
import com.github.shynixn.mccoroutine.bukkit.launch
import com.mineinabyss.idofront.messaging.logVal
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.packy.components.PackyPack
import com.mineinabyss.packy.config.packy
import com.mineinabyss.packy.helpers.CacheMap
import com.mineinabyss.packy.helpers.TemplateIds
import com.mineinabyss.packy.helpers.readPack
import kotlinx.coroutines.*
import net.kyori.adventure.key.Key
import team.unnamed.creative.ResourcePack
import team.unnamed.creative.base.Writable
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter
import team.unnamed.creative.sound.SoundRegistry
import kotlin.io.path.div
import kotlin.io.path.exists

@OptIn(ExperimentalCoroutinesApi::class)
object PackyGenerator {
private val generatorDispatcher = Dispatchers.IO.limitedParallelism(1)
val activeGeneratorJob: MutableMap<TemplateIds, Deferred<PackyPack>> = mutableMapOf()
Expand Down Expand Up @@ -61,14 +62,13 @@ object PackyGenerator {
.mapNotNull { it.path.toFile().readPack() }.forEach { cachedPack.mergeWith(it) }

cachedPack.sortItemOverrides()
if (packy.config.obfuscate) PackObfuscator.obfuscatePack(cachedPack)

MinecraftResourcePackWriter.minecraft().writeToZipFile(packy.plugin.dataFolder.resolve("test.zip"), cachedPack)
MinecraftResourcePackWriter.minecraft().build(cachedPack).let {
val packyPack = PackyPack(it.hash(), packy.config.server.publicUrl(it.hash(), templateIds), it)
cachedPacks[templateIds] = packyPack
cachedPacksByteArray[templateIds] = it.data().toByteArray()
packyPack
PackObfuscator(cachedPack).obfuscatePack()

val builtPack = MinecraftResourcePackWriter.minecraft().build(cachedPack)
MinecraftResourcePackWriter.minecraft().writeToZipFile(packy.plugin.dataFolder.toPath().resolve("test.zip"), cachedPack)
PackyPack(builtPack, templateIds).apply {
cachedPacks[templateIds] = this
cachedPacksByteArray[templateIds] = builtPack.data().toByteArray()
}
}.also {
launch(generatorDispatcher) {
Expand Down
7 changes: 5 additions & 2 deletions src/main/kotlin/com/mineinabyss/packy/components/PackyPack.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.mineinabyss.packy.components

import com.mineinabyss.packy.config.packy
import com.mineinabyss.packy.helpers.TemplateIds
import net.kyori.adventure.resource.ResourcePackInfo
import team.unnamed.creative.BuiltResourcePack
import java.net.URI
import java.util.*

data class PackyPack(val resourcePackInfo: ResourcePackInfo, val builtPack: BuiltResourcePack) {
constructor(hash: String, url: String, builtPack: BuiltResourcePack) :
this(ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(hash.toByteArray()), URI.create(url), hash), builtPack)
constructor(builtPack: BuiltResourcePack, templateIds: TemplateIds) :
this(ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(builtPack.hash().toByteArray()), URI.create(
packy.config.server.publicUrl(builtPack.hash(), templateIds)), builtPack.hash()), builtPack)

}
14 changes: 12 additions & 2 deletions src/main/kotlin/com/mineinabyss/packy/config/PackyConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,22 @@ data class PackyConfig(
@EncodeDefault(ALWAYS) val server: PackyServer = PackyServer(),
@EncodeDefault(ALWAYS) val prompt: String = "",
@EncodeDefault(ALWAYS) val force: Boolean = false,
@EncodeDefault(ALWAYS) val obfuscate: Boolean = false,
@YamlComment("What ObfuscationType to use, valid options are FULL, SIMPLE & NONE")
@EncodeDefault(ALWAYS) val obfuscation: ObfuscationType = ObfuscationType.FULL,
@YamlComment("This will use PackSquash to automatically squash all templates")
@EncodeDefault(ALWAYS) val packSquash: PackSquash = PackSquash(),
@EncodeDefault(ALWAYS) val cachedPackAmount: Int = 18,
@YamlComment(
"The amount of TemplateID combinations Packy should cache the ResourcePack off",
"If your ResourcePack is large it is recommended to lower this value"
)
@EncodeDefault(ALWAYS) val cachedPackAmount: Int = 10,
@EncodeDefault(ALWAYS) val menu: PackyMenu = PackyMenu()
) {

enum class ObfuscationType {
FULL, SIMPLE, NONE
}

@Serializable
data class PackSquash(
val enabled: Boolean = false,
Expand Down
Loading

0 comments on commit 53a4a3d

Please sign in to comment.