Mixins

Note

Cette page s’applique à SpongeCommon, SpongeForge et SpongeVanilla puisque ces trois dépôts utilisent les Mixins pour accrocher aux implémentations sous-jacentes (Serveur Minecraft Vanilla et Forge).

Les Mixins sont une façon de modifier du code java au lancement en ajoutant des comportements additionnels aux classes. Ils permettent d’intégrer des comportements dans des objets Minecraft existants. Les Mixins sont indispensables pour que toutes les implémentations officielles de Sponge fonctionnent.

Une introduction aux principes de base des mixins que nous utilisons pour implémenter Sponge est disponible sur le Wiki Mixin

Ça commence avec les bases. Si vous êtes un développeur Java expérimenté, n’hésitez pas à passer à la section 4, où l’on parle des mixins.

Si vous souhaitez travailler avec les mixins, nous vous recommandons fortement d’examiner avec attention tous les exemples du dépôt SpongeCommon qui est très riche en informations et qui met en scène beaucoup de cas différents et complexes. Vous devriez également consulter la Javadoc du dépôt Mixin lui-même, puisque quasiment tout est déjà documenté.

Note

Le projet Mixin servira à un grand nombre de projets en plus de Sponge en lui-même. De ce fait, Mixin possède sa propre documentation et le dépôt qui va avec.

Mixins et Classes Internes

Même si vous pouvez utiliser des lambdas, des classes anonymes et des classes internes dans les mixins, vous ne pouvez pas utiliser de classes anonymes/classes internes dans d’autres classes anonymes/classe interne qui est aussi dans un mixin, à moins que l’une des classes internes ne soit static.

Ce qui signifie que les expressions comme la suivante ne conduiront qu’à d’horribles erreurs et apporteront mort et destruction sur tout ce qui tente d’utiliser Sponge.

return new Collection<ItemStack>() {
    @Override
    public Iterator<ItemStack> iterator() {
        return new Iterator<ItemStack>() {
            // THIS WILL NOT WORK!

            @Override
            public boolean hasNext() {
                // Code skipped
            }

            @Override
            public ItemStack next() {
                // Code skipped
            }
        };
    }

    // Other methods skipped
};

Cela s’applique à toutes les classes annotées avec @Mixin. Les classes qui ne sont pas touchées par le processeur de mixins peuvent utiliser ces fonctionnalités. Il y a quelques moyens de contourner ces limitations.

Une option serait d’utiliser une classe utilitaire supplémentaire, car contrairement à la classe mixin, cette classe utilitaire existera au runtime, alors que votre mixin sera fusionnée dans la classe visée. Le code suivant fonctionnera donc.

public class SampleCollection implements Collection<ItemStack> {

    private final TargetClass target;

    public SampleCollection(TargetClass target) {
        this.target = target;
    }

    @Override
    public Iterator<ItemStack> iterator() {
        return new Iterator<ItemStack>() {

            @Override
            public boolean hasNext() {
                // Code skipped
            }

            @Override
            public ItemStack next() {
                // Code skipped
            }
        };
    }

    // Other methods skipped
}

@Mixin(TargetClass.class)
public abstract class SomeMixin {
    public Collection<ItemStack> someFunction() {
        return new SampleCollection((TargetClass) (Object) this);
    }
}

L’autre option serait de simplement placer toutes les classes internes directement dans le mixin ou dans l’une de ses méthodes, et non pas les unes dans les autres, par exemple:

@Mixin(TargetClass.class)
public abstract class SomeMixin {

    private final class SampleIterator implements Iterator<ItemStack> {

        @Override
        public boolean hasNext() {
            // Code skipped
        }

        @Override
        public ItemStack next() {
            // Code skipped
        }
    }

    public Collection<ItemStack> someFunction() {
        return new Collection<ItemStack>() {
            @Override
            public Iterator<ItemStack> iterator() {
                return new SampleIterator();
            }

            // Other methods skipped
        };
    }
}