🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
This page is written for:
1.21
This page is written for:
1.21
Die Fabric API bietet ein System, das es Mods erlaubt, auf Aktionen oder Ereignisse zu reagieren, die auch als Events im Spiel definiert sind.
Events sind Hooks, die gemeinsame Anwendungsfälle erfüllen und/oder die Kompatibilität und Leistung zwischen Mods verbessern, die sich in dieselben Bereiche des Codes einklinken. Die Verwendung von Ereignissen ersetzt oft die Verwendung von Mixins.
Die Fabric API stellt Ereignisse für wichtige Bereiche der Minecraft-Codebasis bereit, an denen mehrere Modder interessiert sein könnten.
Ereignisse werden durch Instanzen von net.fabricmc.fabric.api.event.Event
dargestellt, die Callbacks speichern und aufrufen. Oft gibt es eine einzige Event-Instanz für einen Callback, die in einem statischen Attribut EVENT
des Callback-Interfaces gespeichert wird, aber es gibt auch andere Muster. Zum Beispiel fasst ClientTickEvents
mehrere zusammenhängende Ereignisse zusammen.
Callbacks sind ein Teil des Codes, der als Argument an ein Event übergeben wird. Wenn das Event vom Spiel ausgelöst wird, wird der übergebene Teil des Codes ausgeführt.
Jedes Ereignis hat ein entsprechendes Callback-Interface, das üblicherweise <EventName>Callback
genannt wird. Callbacks werden durch den Aufruf der Methode register()
für eine Event-Instanz registriert, wobei eine Instanz des Callbacks als Argument angegeben wird.
Alle Event-Callback-Interfaces, die von der Fabric API bereitgestellt werden, sind im Paket net.fabricmc.fabric.api.event
zu finden.
Dieses Beispiel registriert einen AttackBlockCallback
, um dem Spieler Schaden zuzufügen, wenn er Blöcke trifft, die keinen Gegenstand fallen lassen, wenn sie von Hand abgebaut werden.
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
BlockState state = world.getBlockState(pos);
// Manual spectator check is necessary because AttackBlockCallbacks fire before the spectator check
if (!player.isSpectator() && player.getMainHandStack().isEmpty() && state.isToolRequired()) {
player.damage(world.getDamageSources().generic(), 1.0F);
}
return ActionResult.PASS;
});
Manchmal willst du vielleicht Gegenstände zu Beutetabellen hinzufügen. Zum Beispiel, indem du deine Drops zu einem Vanille-Block oder einer Entität hinzufügst.
Die einfachste Lösung, das Ersetzen der Beutetabellen-Datei, kann andere Mods zerstören. Was ist, wenn sie diese auch ändern wollen? Wir sehen uns an, wie du Gegenstände zu Beutetabellen hinzufügen kannst, ohne die Tabelle zu überschreiben.
Wir werden die Beutetabelle für Kohleerz um Eier erweitern.
Die Fabric API hat ein Event, das ausgelöst wird, wenn Beutetabellen geladen werden, LootTableEvents.MODIFY
. Du kannst einen Callback dafür in deinem Mod-Initialisierer registrieren. Überprüfen wir auch, ob die aktuelle Beutetabelle die Beutetabelle für Kohleerz ist.
LootTableEvents.MODIFY.register((key, tableBuilder, source, registries) -> {
// Let's only modify built-in loot tables and leave data pack loot tables untouched by checking the source.
// We also check that the loot table ID is equal to the ID we want.
In Beutetabellen werden Gegenstände in Beutepool-Einträgen gespeichert, und Einträge werden in Beutepools gespeichert. Um einen Gegenstand hinzuzufügen, müssen wir der Beutetabelle einen Pool mit einem Eintrag für ein Item hinzufügen.
Wir können einen Pool mit LootPool#builder
erstellen, und ihn zur Beutetabelle hinzufügen.
Unser Pool hat auch keine Items, also erstellen wir einen Item-Eintrag mit ItemEntry#builder
und fügen ihn dem Pool hinzu.
LootTableEvents.MODIFY.register((key, tableBuilder, source, registries) -> {
// Let's only modify built-in loot tables and leave data pack loot tables untouched by checking the source.
// We also check that the loot table ID is equal to the ID we want.
if (source.isBuiltin() && COAL_ORE_LOOT_TABLE_ID.equals(key)) {
// We make the pool and add an item
LootPool.Builder poolBuilder = LootPool.builder().with(ItemEntry.builder(Items.EGG));
tableBuilder.pool(poolBuilder);
}
});
In einigen Bereichen des Spiels gibt es keine von der Fabric API bereitgestellten Hooks, so dass du entweder ein Mixin verwenden oder dein eigenes Event erstellen kannst.
Wir werden uns ein Event ansehen, das ausgelöst wird, wenn Schafe geschoren werden. Der Prozess der Erstellung eines Events ist wie folgt:
Das Callback-Interface beschreibt, was von den Event-Listenern implementiert werden muss, die auf dein Event hören werden. Das Callback-Interface beschreibt auch, wie das Event von unserem Mixin ausgelöst werden soll. Es ist üblich, ein Event
-Objekt als Attribut in dem Callback-Interface zu platzieren, das unser tatsächliches Event identifiziert.
Für unsere Event
-Implementierung werden wir uns für ein Array-gestütztes Event entscheiden. Das Array enthält alle Event-Listener, die auf das Event hören.
Unsere Implementierung ruft die Event-Listener der Reihe nach auf, bis einer von ihnen kein ActionResult.PASS
zurückgibt. Das bedeutet, dass ein Listener mit Hilfe seines Rückgabewerts sagen kann: "Abbrechen", "Zustimmen" oder "Egal, überlasse es dem nächsten Event-Listener".
Die Verwendung von ActionResult
als Rückgabewert ist ein konventioneller Weg, um Event-Handler auf diese Weise zusammenarbeiten zu lassen.
Du musst ein Interface erstellen, das eine Event
-Instanz und eine Methode zur Implementierung der Antwort hat. Ein Grundaufbau für unseren Schafschur-Callback ist:
public interface SheepShearCallback {
Event<SheepShearCallback> EVENT = EventFactory.createArrayBacked(SheepShearCallback.class,
(listeners) -> (player, sheep) -> {
for (SheepShearCallback listener : listeners) {
ActionResult result = listener.interact(player, sheep);
if (result != ActionResult.PASS) {
return result;
}
}
return ActionResult.PASS;
});
ActionResult interact(PlayerEntity player, SheepEntity sheep);
}
Schauen wir uns das genauer an. Wenn der Invoker aufgerufen wird, wird über alle Listener iteriert:
(listeners) -> (player, sheep) -> {
for (SheepShearCallback listener : listeners) {
Dann rufen wir unsere Methode (in diesem Fall interact
) auf dem Listener auf, um seine Antwort zu erhalten:
ActionResult interact(PlayerEntity player, SheepEntity sheep);
Wenn der Listener sagt, dass wir abbrechen (ActionResult.FAIL
) oder vollständig beenden (ActionResult.SUCCESS
) müssen, gibt der Callback das Ergebnis zurück und beendet die Schleife. ActionResult.PASS
geht zum nächsten Listener über und sollte in den meisten Fällen zum Erfolg führen, wenn keine weiteren Listener registriert sind:
if (result != ActionResult.PASS) {
return result;
}
}
return ActionResult.PASS;
Wir können Javadoc-Kommentare an die oberste Stelle der Callback-Klassen setzen, um zu dokumentieren, was jedes ActionResult
macht. In unserem Fall könnte das wie folgt sein:
/**
* Callback for shearing a sheep.
* Called before the sheep is sheared, items are dropped, and items are damaged.
* Upon return:
* - SUCCESS cancels further processing and continues with normal shearing behavior.
* - PASS falls back to further processing and defaults to SUCCESS if no other listeners are available
* - FAIL cancels further processing and does not shear the sheep.
*/
Jetzt haben wir das Grundgerüst für ein Event, aber wir müssen es auslösen. Da wir das Ereignis auslösen wollen, wenn ein Spieler versucht, ein Schaf zu scheren, rufen wir das Ereignis invoker
in SheepEntity#interactMob
auf, wenn sheared()
aufgerufen wird (d.h.
@Mixin(SheepEntity.class)
public class SheepEntityMixin {
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V"), method = "interactMob", cancellable = true)
private void onShear(final PlayerEntity player, final Hand hand, final CallbackInfoReturnable<ActionResult> info) {
ActionResult result = SheepShearCallback.EVENT.invoker().interact(player, (SheepEntity) (Object) this);
if (result == ActionResult.FAIL) {
info.setReturnValue(result);
}
}
}
Jetzt müssen wir unser Event testen. Du kannst einen Listener in deiner Initialisierungsmethode (oder in einem anderen Bereich, wenn du das bevorzugst) registrieren und dort benutzerdefinierte Logik hinzufügen. Hier ist ein Beispiel, bei dem dem ein Diamant anstelle von Wolle auf die Füße des Schafs fällt:
SheepShearCallback.EVENT.register((player, sheep) -> {
sheep.setSheared(true);
// Create diamond item entity at sheep's position.
ItemStack stack = new ItemStack(Items.DIAMOND);
ItemEntity itemEntity = new ItemEntity(player.getWorld(), sheep.getX(), sheep.getY(), sheep.getZ(), stack);
player.getWorld().spawnEntity(itemEntity);
return ActionResult.FAIL;
});
Wenn du im Spiel ein Schaf scherst, sollte anstelle von Wolle ein Diamant fallen.