Jak działa Mixins?

Informacja

Niniejsza publikacja dotyczy SpongeCommon, SpongeForge, oraz SpongeVanilla jako to, że wszystkie trzy wykorzystują zaczep udostępniony przez oprogramowanie Mixin do podstawowej implementacji (Vanilla Minecraft serwer i Forge).

Mixin jest metodą modyfikacji kodu Java podczas jego działania poprzez uruchomienie dodatkowych Eventów. Umożliwiają one włączenie do Sponge istniejących już funkcji i obiektów w Minecraft. Mixins jest niezbędne do poprawnego działania modyfikacji Sponge.

Podstawowe objaśnienie koncepcji, która wspiera podstawową funkcjonalność w Sponge jest dostępna na Mixin Wiki

Zaczyna się od zupełnych podstaw. Jeśli jesteś zaawansowanym deweloperem Java, spokojnie możesz przejść do sekcji 4, gdzie Mixins zaczynają być dokładnie omawiane.

If you’re looking to get started writing mixins, we also strongly recommend carefully examining all of the examples in the SpongeCommon repository which are extensively documented and cover many of the more complex scenarios. You should also consult the Javadoc of the Mixin repository itself, since almost everything is already documented.

Informacja

The Mixin project will be servicing a number of other projects in addition to Sponge itself. Therefore, Mixin has its own documentation together with the repository.

Mixins and Inner Classes

While you can use lambdas, anonymous and inner classes inside mixins, you cannot use an anonymous/inner class within another anonymous/inner class that is also inside a mixin, unless one of the inner classes is static.

Oznacza to, że wyrażenia podobne do tych poniżej spowodują problem z Mixiny. Sprowadzą śmierć i zniszczenie dlatego będzie trzeba użyć gąbki (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
};

This applies to all classes that are annotated with @Mixin. Classes that are not touched by the mixin processor may make use of those features. There are two ways to work around this.

Jedną z opcji jest użycie oddzielnej klasy użytkowej, ponieważ w przeciwieństwie do twojej klasy mixin, klasa użytkowa będzie nadal istnieć w środowisku wykonawczym, podczas gdy klasa mixin zostanie przyłączona do określonej klasy docelowej. W związku z tym poniższy kod zadziała.

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);
    }
}

Inną opcją jest umieszczenie wszystkich zagnieżdżonych klas wewnętrznych bezpośrednio w mix-inie lub jednej z jego metod, w przeciwieństwie do jednej w drugiej. Na przykład:

@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
        };
    }
}