Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f20acad
fix: wrong usage for hash
SettingDust Apr 22, 2025
37fa11c
feat(mixin): add mixin recording feature
SettingDust Apr 22, 2025
c223ca7
chore(forge): remove signature file
SettingDust Apr 23, 2025
3ca18b4
perf(mixin): faster path concat
SettingDust Apr 23, 2025
8cd25de
feat(mixin): add logger
SettingDust Apr 23, 2025
85320d8
feat(mixin): use input classpath in service
SettingDust Apr 23, 2025
820fcd6
fix(mixin): use config as recorded key
SettingDust Apr 23, 2025
880b214
fix(mixin): detect more refmap json
SettingDust Apr 23, 2025
8fac40c
refactor(mixin): runtime refmap remap
SettingDust Apr 23, 2025
54907e6
feat(mixin): cache the applied mixins
SettingDust Apr 24, 2025
e01425d
fix(mixin): null pointer since the tiny mapping may have null if need…
SettingDust Apr 24, 2025
6dd0778
fix(mixin): null pointer since the mixin can target null name method
SettingDust Apr 24, 2025
5d9b19b
chore(mixin): add source for mixin config
SettingDust Apr 24, 2025
fa52067
fix(mixin): clear the registered config missing before
SettingDust Apr 25, 2025
568ddd7
fix(mixin): clear the processor mixins missing before
SettingDust Apr 25, 2025
30ec964
fix(mixin): clear the processor environment
SettingDust Apr 25, 2025
b203e4a
Merge remote-tracking branch 'MsRandom/HEAD' into feature/build-time-…
SettingDust Jun 4, 2025
8fecfbd
Merge remote-tracking branch 'MsRandom/main' into feature/build-time-…
SettingDust Jul 12, 2025
2727cb3
Merge remote-tracking branch 'MsRandom/main' into feature/build-time-…
SettingDust Mar 23, 2026
052de6d
feat(mixin): isolated mixin executor
SettingDust Mar 23, 2026
8c345a7
Merge remote-tracking branch 'MsRandom/main' into feature/build-time-…
SettingDust Mar 29, 2026
a45f6f1
feat(mixin): handle the fabric compat version
SettingDust Mar 29, 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
2 changes: 1 addition & 1 deletion minecraft-codev-access-widener/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.6.3
version=0.6.4
2 changes: 1 addition & 1 deletion minecraft-codev-core/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.6.9
version=0.6.10
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.msrandom.minecraftcodev.core.utils

import com.google.common.collect.Multimaps
import com.google.common.collect.SetMultimap
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.mapSerialDescriptor
import kotlinx.serialization.descriptors.setSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

@Suppress("UnstableApiUsage")
@OptIn(ExperimentalSerializationApi::class)
class SetMultimapSerializer<K, V>(
private val keySerializer: KSerializer<K>,
private val valueSerializer: KSerializer<V>,
private val valueFactory: () -> MutableSet<V> = { HashSet() }
) : KSerializer<SetMultimap<K, V>> {
override val descriptor =
SerialDescriptor(
"Multimap",
mapSerialDescriptor(
keySerializer.descriptor, setSerialDescriptor(valueSerializer.descriptor)
)
)

override fun serialize(encoder: Encoder, value: SetMultimap<K, V>) {
MapSerializer(keySerializer, SetSerializer(valueSerializer)).serialize(encoder, Multimaps.asMap(value))
}

override fun deserialize(decoder: Decoder): SetMultimap<K, V> {
val map = Multimaps.newSetMultimap<K, V>(HashMap(), valueFactory)
for (entry in MapSerializer(keySerializer, SetSerializer(valueSerializer)).deserialize(decoder)) {
map.putAll(entry.key, entry.value)
}
return map
}
}
2 changes: 1 addition & 1 deletion minecraft-codev-decompiler/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.6.0
version=0.6.1
2 changes: 1 addition & 1 deletion minecraft-codev-fabric/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.7.0
version=0.7.1
2 changes: 1 addition & 1 deletion minecraft-codev-forge/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.8.3
version=0.8.4
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class ForgeMixinListingRule : MixinListingRule {
val mixinConfigsString = manifest.mainAttributes.getValue(MANIFEST_MIXINS_CONFIG)
?: return loadFromToml(directory)

val mixinConfigs = mixinConfigsString.split(",").map(String::trim)
val mixinConfigs = mixinConfigsString.split(",").map(String::trim).filter(String::isNotBlank)

return ForgeMixinConfigHandler(mixinConfigs, true)
}
Expand Down
2 changes: 1 addition & 1 deletion minecraft-codev-includes/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.6.5
version=0.6.6
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package net.msrandom.minecraftcodev.includes

import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
Expand Down Expand Up @@ -61,7 +64,7 @@ abstract class ExtractIncludes : TransformAction<TransformParameters.None> {
classpath
.map { async { hashFile(it.toPath()) } }
.awaitAll()
.toSet()
.toHashSet()
}

for (includedJar in handler.list(root)) {
Expand Down
4 changes: 3 additions & 1 deletion minecraft-codev-mixins/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ gradlePlugin {
}

dependencies {
api(group = "net.fabricmc", name = "sponge-mixin", version = "0.15.0+mixin.0.8.7")
api(group = "net.fabricmc", name = "sponge-mixin", version = "0.17.0+mixin.0.8.7")
api(group = "io.github.llamalad7", name = "mixinextras-common", version = "0.5.3")
api(group = "net.fabricmc", name = "mapping-io", version = "0.7.1")

implementation(projects.minecraftCodevCore)
}
Expand Down
2 changes: 1 addition & 1 deletion minecraft-codev-mixins/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.6.0
version=0.6.2
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,18 @@ package net.msrandom.minecraftcodev.mixins
import net.msrandom.minecraftcodev.core.utils.applyPlugin
import net.msrandom.minecraftcodev.core.utils.createSourceSetConfigurations
import net.msrandom.minecraftcodev.core.utils.disambiguateName
import net.msrandom.minecraftcodev.mixins.mixin.GradleMixinService
import org.gradle.api.Plugin
import org.gradle.api.plugins.PluginAware
import org.gradle.api.tasks.SourceSet
import org.spongepowered.asm.launch.MixinBootstrap
import org.spongepowered.asm.mixin.MixinEnvironment
import org.spongepowered.asm.service.MixinService

val SourceSet.mixinsConfigurationName get() = disambiguateName(MinecraftCodevMixinsPlugin.MIXINS_CONFIGURATION)

class MinecraftCodevMixinsPlugin<T : PluginAware> : Plugin<T> {
override fun apply(target: T) =
applyPlugin(target) {
createSourceSetConfigurations(MIXINS_CONFIGURATION)

MixinBootstrap.init()
(MixinService.getService() as GradleMixinService).phaseConsumer.accept(MixinEnvironment.Phase.DEFAULT)
// Mixin initialization is now done in IsolatedMixinExecutor
// within an isolated classloader for each mixin operation
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,50 @@
package net.msrandom.minecraftcodev.mixins

import com.google.common.collect.HashMultimap
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import net.msrandom.minecraftcodev.core.utils.SetMultimapSerializer
import net.msrandom.minecraftcodev.core.utils.toPath
import net.msrandom.minecraftcodev.core.utils.zipFileSystem
import org.gradle.api.artifacts.transform.*
import org.gradle.api.artifacts.transform.CacheableTransform
import org.gradle.api.artifacts.transform.InputArtifact
import org.gradle.api.artifacts.transform.TransformAction
import org.gradle.api.artifacts.transform.TransformOutputs
import org.gradle.api.artifacts.transform.TransformParameters
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileSystemLocation
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import java.nio.file.StandardCopyOption
import kotlin.io.path.copyTo
import kotlin.io.path.deleteExisting
import kotlin.io.path.extension
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.readLines
import kotlin.io.path.writeLines

@CacheableTransform
abstract class StripMixins : TransformAction<TransformParameters.None> {
abstract class StripMixins : TransformAction<StripMixins.Parameters> {
abstract class Parameters : TransformParameters {
abstract val appliedMixins: ConfigurableFileCollection
@InputFiles
@PathSensitive(PathSensitivity.NONE)
get

init {
appliedMixins.convention()
}
}

abstract val inputFile: Provider<FileSystemLocation>
@InputArtifact
@PathSensitive(PathSensitivity.NONE)
get

@OptIn(ExperimentalSerializationApi::class)
override fun transform(outputs: TransformOutputs) {
val input = inputFile.get().toPath()

Expand All @@ -38,13 +63,37 @@ abstract class StripMixins : TransformAction<TransformParameters.None> {
return
}

val output = outputs.file("${input.nameWithoutExtension}-no-mixins.${input.extension}").toPath()
val appliedMixins = HashMultimap.create<String, String>()
val serializer = SetMultimapSerializer(String.serializer(), String.serializer())
for (multimap in parameters.appliedMixins.map { Json.decodeFromStream(serializer, it.inputStream()) }) {
appliedMixins.putAll(multimap)
}

val needStrip = zipFileSystem(input).use { fs ->
val root = fs.getPath("/")
handler.list(root).any { appliedMixins.containsKey(it) }
}

if (!needStrip) {
outputs.file(inputFile)

return
}

val output = outputs.file("${input.nameWithoutExtension}-mixins-stripped.${input.extension}").toPath()

input.copyTo(output, StandardCopyOption.COPY_ATTRIBUTES)

zipFileSystem(output).use {
zipFileSystem(output).use { it ->
val root = it.getPath("/")
handler.list(root).forEach { path -> root.resolve(path).deleteExisting() }
handler.list(root)
.mapNotNull { path ->
appliedMixins[path].takeIf { it.isNotEmpty() }?.let { it to path }
}
.forEach { (mixins, path) ->
val path = root.resolve(path)
path.writeLines(path.readLines().filterNot { line -> mixins.any { line.contains(it) } })
}
handler.remove(root)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.msrandom.minecraftcodev.mixins.mixin

import org.slf4j.LoggerFactory
import org.spongepowered.asm.logging.Level
import org.spongepowered.asm.logging.LoggerAdapterAbstract

class GradleMixinLogger(val name: String) : LoggerAdapterAbstract(name) {
companion object {
private const val PREFIX = "[Mixin] "
}

private val logger = LoggerFactory.getLogger(name)

override fun getType() = "Gradle Logger"

override fun catching(level: Level, t: Throwable) =
logger.info("${PREFIX}Catching {}: {}", t.javaClass.getName(), t.message, t)

override fun log(level: Level, message: String, vararg params: Any) {
val level = when (message) {
"Mixin environment was unable to detect the current side, sided mixins will not be applied" -> Level.INFO
else -> level
}
val message = PREFIX + message
when (level) {
Level.TRACE -> logger.trace(message, *params)
Level.DEBUG -> logger.debug(message, *params)
Level.INFO -> logger.info(message, *params)
Level.WARN -> logger.warn(message, *params)
Level.ERROR -> logger.error(message, *params)
Level.FATAL -> logger.error("[FATAL] $message", *params)
}
}

override fun log(level: Level, message: String, t: Throwable) {
log(level, message, t as Any)
}

override fun <T : Throwable> throwing(t: T): T {
logger.warn("${PREFIX}Throwing {}: {}", t.javaClass.getName(), t.message, t)
return t
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.msrandom.minecraftcodev.mixins.mixin

import com.google.common.collect.SetMultimap
import org.objectweb.asm.tree.ClassNode
import org.spongepowered.asm.mixin.MixinEnvironment
import org.spongepowered.asm.mixin.extensibility.IMixinInfo
import org.spongepowered.asm.mixin.transformer.ext.IExtension
import org.spongepowered.asm.mixin.transformer.ext.ITargetClassContext
import java.util.*

class GradleMixinRecorderExtension : IExtension {
companion object {
private val targetClassContextClass =
Class.forName("org.spongepowered.asm.mixin.transformer.TargetClassContext")
private val mixinsField = targetClassContextClass.getDeclaredField("mixins")

init {
mixinsField.isAccessible = true
}
}

var appliedMixins: SetMultimap<String, String>? = null

override fun checkActive(environment: MixinEnvironment?) = true

override fun preApply(context: ITargetClassContext) {}

override fun postApply(context: ITargetClassContext) {
val mixins = mixinsField.get(context) as SortedSet<IMixinInfo>
if (mixins.isNotEmpty()) {
for (info in mixins) {
appliedMixins!!.put(info.config.name, info.name)
}
}
}

override fun export(
env: MixinEnvironment?,
name: String?,
force: Boolean,
classNode: ClassNode?
) {
}
}
Loading