Mixin Configuration - Harveykang/AsyncParticles GitHub Wiki

请帮忙翻译这个页面

Path

config/asyncparticles/asyncparticles-mixin.properties

Default

particle$noLightCache=dev.shadowsoffire.gateways.client.GatewayParticle,\
com.chailotl.particular.particles.FireflyParticle,com.lowdragmc.photon.client.gameobject.FXObject,\
net.diebuddies.minecraft.weather.WeatherParticle
particle$noCulling=pigcart.particlerain.particle.GroundFogParticle,\
com.lowdragmc.photon.client.gameobject.FXObject
particle$lockRequired=yesman.epicfight.client.particle.TrailParticle,\
com.dfdyz.epicacg.client.particle.BloomTrailParticle,\
com.brandon3055.draconicevolution.client.render.effect.ExplosionFX,\
com.brandon3055.draconicevolution.client.render.effect.CrystalFXWireless,\
com.lowdragmc.photon.client.gameobject.emitter.Emitter,\
com.lowdragmc.photon.client.gameobject.emitter.particle.ParticleEmitter,\
com.lowdragmc.photon.client.gameobject.emitter.beam.BeamEmitter,\
com.lowdragmc.photon.client.gameobject.emitter.trail.TrailEmitter
particle$lockProvider=yesman.epicfight.client.particle.TrailParticle,\
com.dfdyz.epicacg.client.particle.BloomTrailParticle,\
com.brandon3055.draconicevolution.client.render.effect.ExplosionFX,\
com.brandon3055.draconicevolution.client.render.effect.CrystalFXWireless,\
com.lowdragmc.photon.client.gameobject.FXObject

'particle$noCulling'

  • 对特定的粒子类禁用剔除

'particle$noLightCache'

  • 对特定的粒子类禁用光照缓存

'particle$lockProvider' and 'particle$lockRequired'

  • 提供锁或需要加锁的粒子类
  • 用于在tickrender方法上加一个自旋锁
  • 有时一个类不会同时实现tickrender方法,此时需要:
    • 将实现了实现tickrender方法的最终子类加入'particle$lockRequired'
    • 将实现了实现tickrender方法的最终子类最小公共父类加入'particle$lockProvider'

实现原理

在预启动阶段读取原始Mixin类文件的字节码,修改其目标类和类名,生成一份字节码。

因为Mixin的本质不是Java类,不会被JVM加载,因此只需保存一个“生成的完整类名->byte[]”的Map即可(未来可能会将它持久化以减少内存占用),且不需要严格遵守类文件格式。

每个xxx.mixins.json对应一个MixinConfigIMixinConfigPlugin

IMixinConfigPlugin可以通过getMixins方法提供一份额外Mixin类的短名列表。

Mixin被应用时会从MixinConfig拿到MixinService拿到ClassBytecodeProvider,调用getClassNode,入参为Mixin的完整内部类名,其中就包括我们在getMixins提供的额外Mixin类。

将我们的xxx.mixins.json对应的MixinConfig中的MixinService替换为我们的包装对象,这样在调用getClassNode方法时,先检查类名是否包含在上文提到的Map中,如果在,就返回修改过的字节码,转为ClassNode。这样我们运行时生成的Mixin类就会被应用,从而达到修改目标类的目的。

另外还需要通过MixinSquared的MixinCanceller取消原始Mixin的注入。

具体实现见此处

The following content was translated by Qwen 3.

'particle$noCulling'

  • Disables culling for specific particle classes

'particle$noLightCache'

  • Disables light caching for specific particle classes

'particle$lockProvider' and 'particle$lockRequired'

  • Particle classes that provide or require a lock
  • Used to add a spinlock to the tick and render methods
  • Sometimes a class does not implement both tick and render methods, in which case:
    • Add the final subclass that implements either tick or render to 'particle$lockRequired'
    • Add the minimal common parent class of that final subclass to 'particle$lockProvider'

Implementation Details

During the pre-launch phase, the bytecode of the original Mixin class file is read. The target class and class name are modified to generate new bytecode.

Since Mixins are not actual Java classes and will not be loaded by the JVM, it's sufficient to maintain a Map from generated full class names to byte arrays (in the future, this may be persisted to reduce memory usage), and there is no strict need to follow the class file format.

Each xxx.mixins.json corresponds to a MixinConfig and an IMixinConfigPlugin.

The IMixinConfigPlugin can provide an additional list of mixin via the getMixins method.

When a Mixin is applied, it retrieves the MixinService from the MixinConfig, then gets the ClassBytecodeProvider, and calls getClassNode. The parameter is the full internal name of the applying Mixin. The additional Mixins also pass through here.

By replacing the MixinService in the MixinConfig corresponding to our xxx.mixins.json with our wrapper object, during the call to getClassNode, we first check whether the class name exists in the aforementioned Map. If so, we return the modified bytecode converted into a ClassNode. This allows our runtime-generated Mixin classes to be applied, thus achieving the goal of modifying the target class.

Additionally, the original Mixin injection must be canceled using MixinSquared's MixinCanceller.

The specific implementation can be found here.