Mixin

repository·master·Indexed 23 days ago

https://github.com/spongepowered/mixin

A Java trait/mixin framework that uses ASM to hook into the runtime classloading process, allowing developers to modify existing bytecode via pluggable services. It includes an Annotation Processor for handling obfuscation tasks and supports integration with Gradle, Eclipse, and IntelliJ IDEA. The framework provides tools like AnnotatedMixin for managing metadata and Mappings for handling field and method obfuscation data.

Tokens
2.9K
Snippets
4
Records
15
Agent score
83%

What's inside spongepowered-mixin

  1. Overview of Mixin Framework

    master
    Mixin is a trait/mixin framework for Java that utilizes ASM to hook into the runtime classloading process. It uses a set of pluggable services (built-in or user-provided) to perform transformations. While it supports Mojang's LegacyLauncher, it is increasingly used with ModLauncher for better extensibility and support for Java 8 and later.
  2. Configure Mixin Annotation Processor in Eclipse

    master

    To receive context-sensitive errors and warnings in Eclipse, you can integrate the Mixin Annotation Processor by following these steps:

    1. Run gradle build to generate the mixin jar.
    2. In your Eclipse project properties, navigate to Java Compiler -> Annotation Processing -> Factory Path.
    3. Check Enable project specific settings.
    4. Click Add External JARs and select the generated mixin jar with the -processor suffix (typically found in Mixin/build/libs).
    5. Navigate to Java Compiler -> Annotation Processing.
    6. Check Enable project specific settings and Enable annotation processing.
    7. Next to Processor options, click New... and set:
      • Key: reobfSrgFile
      • Value: The fully-qualified path to your mcp-srg.srg file.
    8. Click OK to apply.
  3. Configure the Mixin Annotation Processor in Gradle

    master

    Mixin provides an Annotation Processor (AP) to handle obfuscation tasks at compile time by generating mappings.

    If you are using Gradle 5 or later, you must explicitly specify the annotation processor using the annotationProcessor configuration. Mixin provides "fat jar" artifacts containing all required dependencies via the :processor classifier.

    For example, if your dependency is org.spongepowered:mixin:1.2.3, your configuration should look like this:

    dependencies {
        implementation 'org.spongepowered:mixin:1.2.3'
        annotationProcessor 'org.spongepowered:mixin:1.2.3:processor'
    }

    If you are working on a Minecraft Forge project, the MixinGradle plugin can be used to simplify this configuration.

  4. AnnotatedMixin class overview

    master
    AnnotatedMixin is a class used during the Mixin annotation processing phase to store and manage information about a Mixin class. It implements IMixinContext and IAnnotatedElement, acting as a central repository for a Mixin's metadata, including its targets, methods, annotations, and handlers for various Mixin features like @Inject, @Shadow, @Overwrite, and @Accessor. It is primarily used by the Mixin annotation processor to validate and prepare Mixins for remapping and bytecode transformation.
  5. Access Mixin Binaries and Maven Repositories

    master

    Mixin binaries are available via Jenkins or through the following Maven repositories:

    • https://repo.spongepowered.org/repository/maven-public/ (Contains both SNAPSHOTs and RELEASE builds)
    • https://files.minecraftforge.net/maven/ (Contains RELEASE builds only)
  6. Register an @Inject in AnnotatedMixin

    master

    Use registerInjector to register an injection point (e.g., using the @Inject annotation) on a method. This method automatically handles the registration of @At selectors and @Slice coordinates if they are present on the annotation.

    // Example usage
    annotatedMixin.registerInjector(method, injectAnnotation, remapOption);
    public void registerInjector(ExecutableElement method, AnnotationHandle inject, InjectorRemap remap) {
        this.removeMethod(method);
        AnnotatedElementInjector injectorElement = new AnnotatedElementInjector(method, inject, this, remap);
        this.injectors.registerInjector(injectorElement);
    
    List<IAnnotationHandle> ats = inject.getAnnotationList("at");
        for (IAnnotationHandle at : ats) {
            this.registerInjectionPoint(method, inject, "at", (AnnotationHandle)at, remap, "@At(%s)");
        }
    
    List<IAnnotationHandle> slices = inject.getAnnotationList("slice");
        for (IAnnotationHandle slice : slices) {
            String id = slice.<String>getValue("id", "");
            String coord = "slice";
            if (!Strings.isNullOrEmpty(id)) {
                coord += "." + id;
            }
            SelectorAnnotationContext sliceContext = new SelectorAnnotationContext(injectorElement, slice, coord);
    
            IAnnotationHandle from = slice.getAnnotation("from");
            if (from != null) {
                this.registerSliceInjectionPoint(method, inject, "from", (AnnotationHandle)from, remap, "@Slice[" + id + "](from=@At(%s))",
                        sliceContext);
            }
            IAnnotationHandle to = slice.getAnnotation("to");
            if (to != null) {
                this.registerSliceInjectionPoint(method, inject, "to", (AnnotationHandle)to, remap, "@Slice[" + id + "](to=@At(%s))",
                        sliceContext);
            }
        }
    }
  7. Register an @Accessor in AnnotatedMixin

    master

    Use registerAccessor to register an accessor method (e.g., using the @Accessor annotation) that provides access to a field in a target class.

    annotatedMixin.registerAccessor(element, accessorAnnotation, shouldRemap);
    public void registerAccessor(Element element, AnnotationHandle accessor, boolean shouldRemap) {
        this.removeMethod(element);
        this.accessors.registerAccessor(new AnnotatedElementAccessor(element, accessor, this, shouldRemap));
    }
  8. Ensure mapping uniqueness with asUnique()

    master

    When consuming mappings, you may want to prevent conflicts where the same source field or method is mapped to different destinations. Wrapping your Mappings instance with asUnique() provides a UniqueMappings decorator that validates uniqueness.

    If a conflict is detected (i.e., the same source element is being mapped to a different destination than previously recorded), a MappingConflictException is thrown.

  9. Retrieve Mixin metadata from AnnotatedMixin

    master

    The AnnotatedMixin class provides several methods to inspect the Mixin's properties during the annotation processing phase:

    • getAnnotation(): Returns the IAnnotationHandle for the @Mixin annotation.
    • getMixinElement(): Returns the TypeElement representing the Mixin class.
    • getHandle(): Returns the TypeHandle for the Mixin class.
    • getClassRef(): Returns the internal bytecode name of the Mixin class.
    • getTargetClassName(): Returns the name of the primary target class.
    • getTargetClassRef(): Returns the name of the primary target class.
    • getTargets(): Returns a List<TypeHandle> of all targets this Mixin applies to.
    • isMultiTarget(): Returns true if the Mixin targets more than one class.
    • isInterface(): Returns true if the Mixin class is an interface.
    • remap(): Returns true if remapping should be applied to annotations in this Mixin.
  10. Use Mappings to manage obfuscation mapping data

    master

    The Mappings class is a reference implementation of IMappingConsumer used to store and manage field and method mappings for different ObfuscationTypes. It allows you to add mappings for fields and methods and retrieve them as MappingSets.

    To ensure that no single source element maps to multiple destination elements (which would cause conflicts), you can wrap the Mappings instance in a UniqueMappings consumer using the asUnique() method.