Dynamische Registry 26.2
Eine Einführung in dynamische Registries - was sie sind, wann sie nützlich sind und wie du mit der Fabric-API deine eigene erstellen kannst.
Eine Registry ist ein zentrales "Telefonbuch", welches einzigartige IDs, wie minecraft:items zu spezifischen Objekten zuweist.
Es gibt zwei Arten von Registries: Statische Registries, wie beispielsweise die Block- und die Item-Registries, werden beim Start eingefroren, während dynamische oder benutzerdefinierte Registries zur Laufzeit aus JSON-Dateien in Datenpaketen gefüllt werden.
Sie sind aus mehreren Gründen nützlich:
- Die trennen Logik vom Inhalt.
- Andere Modder können neue Inhalte über Datenpakete hinzufügen, anstatt deinen Code zu patchen.
- Spieler können Standardwerte wie Manakosten oder Upgrade-Preise überschreiben, indem sie Dateneinträge in einem Datenpaket ersetzen.
- Daten einer dynamischen Registry sind weltspezifisch. Sie werden beim Öffnen einer Welt geladen und beim Schließen dieser Welt geleert.
- Dynamische Registries lösen das Problem der "fest codierten Inhalte". Anstatt jede Fertigkeit, jede Quest oder jedes Upgrade direkt in Java-Code mit Enums oder statischen Listen zu verankern, definierst du im Code eine Vorlage und lässt den eigentlichen Inhalt aus den Daten stammen.
Lasst uns eine dynamische Registry für ein magisches Fertigkeitensystem erstellen.
Aufbau der Klasse
Erstelle zunächst die Klasse, die einen Eintrag in der Registry darstellt. Es ist ein einfacher Datenspeicher für Werte, die mit den einzelnen magischen Fähigkeiten verknüpft sind, wie Name, Manakosten usw. Es wird ein Codec benötigt, um den Eintrag zu kodieren oder dekodieren.
java
public record MagicSkillsRegistryEntry(String name, int manaCost, Optional<CacheableFunction> onUseMcFunction) {
public static final Codec<MagicSkillsRegistryEntry> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.STRING.fieldOf("name").forGetter(MagicSkillsRegistryEntry::name),
Codec.INT.fieldOf("mana_cost").forGetter(MagicSkillsRegistryEntry::manaCost),
CacheableFunction.CODEC.optionalFieldOf("on_use_mc_function").forGetter(MagicSkillsRegistryEntry::onUseMcFunction)
).apply(instance, MagicSkillsRegistryEntry::new)
);
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
nameist der Name der Fähigkeit.manaCostsind die Manakosten der Fähigkeit.onUseMcFunctionist eine Funktion, die der Server ausführen kann, wenn die Fähigkeit verwendet wird. Wenn dies in der Registry hinterlegt ist, können andere Datapackete die Logik beliebiger Fähigkeiten anpassen oder neue Fähigkeiten mit benutzerdefinierten Funktionen hinzufügen.
Die Registry registrieren
Jede Registry ist mit einem Schlüssel registriert, der sie eindeutig identifiziert. Erstellen wir also diesen Schlüssel und eine Klasse, die ihn enthält. Wir nennen diese Klasse ExampleModRegistries:
TIP
Es wird empfohlen, die Registryschlüssel in einer allgemeinen Klasse zu deklarieren, da dies die Verwaltung mehrerer Registries erleichtert.
java
public class ExampleModRegistries {
public static final ResourceKey<Registry<MagicSkillsRegistryEntry>> MAGIC_SKILLS_REGISTRY_KEY =
ResourceKey.createRegistryKey(ExampleMod.id("magic_skills_registry"));
public static void initialize() {
// Register Code Here
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Rufe ExampleModRegistries.initialize() von deinem Mod Initialisierer auf.
java
public class ExampleModDynamicRegistries implements ModInitializer {
@Override
public void onInitialize() {
ExampleModRegistries.initialize();
}
}1
2
3
4
5
6
2
3
4
5
6
Registriere dann bei den DynamicRegistries der Fabric-API, die zwei verschiedene Strategien bieten: DynamicRegistries.register() oder DynamicRegistries.registerSynced().
Verwendung von register()
DynamicRegistries.register() erstellt eine nicht synchronisierte Registry. Sie wird nur auf dem Server geladen und ist auf dem Client nicht verfügbar. Verwende dies, wenn der Client die Registry nie lesen muss.
In unserem Beispiel spielt das keine Rolle, aber so macht man es:
java
DynamicRegistries.register(MAGIC_SKILLS_REGISTRY_KEY, MagicSkillsRegistryEntry.CODEC);1
Verwendung von registerSynced()
INFO
Die in den folgenden Methoden verwendeten Schlüssel werden über denselben Weg erstellt wie wir MAGIC_SKILLS_REGISTRY_KEY erstellt haben, allerdings mit einem anderen Namen.
DynamicRegistries.registerSynced() erstellt eine synchronisierte Registry. Wenn ein Client einer Welt beitritt, synchronisiert der Server die Daten dieser Registry automatisch mit dem Client. Verwende dies, wenn der Client die Daten für das Rendering, die Benutzeroberfläche, Tooltips oder andere clientseitige Logik benötigt.
java
DynamicRegistries.registerSynced(MAGIC_SKILLS_SYNCED_REGISTRY_KEY, MagicSkillsRegistryEntry.CODEC);1
DynamicRegistries.registerSynced() hat eine Überladung, die einen zweiten Codec für die clientseitige Dekodierung akzeptiert. Dies ist nützlich, wenn der Client nicht jedes Feld des vollständigen Server-Eintrags benötigt.
In unserem Fall benötigen wir auf der Client-Seite nur die Felder name und manaCost. Lasst uns also einen Codec erstellen, der onUseMcFunction nicht enthält, und übergeben wir diesen Codec an registerSynced:
java
public record MagicSkillsRegistryEntry(String name, int manaCost, Optional<CacheableFunction> onUseMcFunction) {
// Other Variables and Methods
public static final Codec<MagicSkillsRegistryEntry> CLIENT_CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.STRING.fieldOf("name").forGetter(MagicSkillsRegistryEntry::name),
Codec.INT.fieldOf("mana_cost").forGetter(MagicSkillsRegistryEntry::manaCost)
).apply(instance, (name, manaCost) -> new MagicSkillsRegistryEntry(name, manaCost, Optional.empty()))
);
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
java
DynamicRegistries.registerSynced(MAGIC_SKILLS_DOUBLE_CODEC_REGISTRY_KEY, MagicSkillsRegistryEntry.CODEC, MagicSkillsRegistryEntry.CLIENT_CODEC);1
SyncOption
Beide Überladungen von DynamicRegistries.registerSynced() akzeptieren am Ende SyncOption-Argumente, um das Synchronisationsverhalten zu konfigurieren. Die einzige verfügbare Option ist:
SKIP_WHEN_EMPTY: Synchronisiert die Registry nur, wenn sie Einträge beinhaltet. Dies kann die Kompatibilität für Clients verbessern, die die Registry möglicherweise nicht benötigen.
Beispiel:
java
DynamicRegistries.registerSynced(MAGIC_SKILLS_WITH_OPTION_REGISTRY_KEY, MagicSkillsRegistryEntry.CODEC, DynamicRegistries.SyncOption.SKIP_WHEN_EMPTY);1
Befüllen der Registry
JSON-Dateien werden zum Erstellen von Registryeinträgen verwendet. Die JSON-Struktur muss mit dem MagicSkillsRegistryEntry übereinstimmen. In diesem Beispiel verfügt unsere Eintragsklasse über drei Felder, sodass die JSON-Datei für den Eintrag healing_skill etwa wie folgt aussehen könnte:
json
{
"mana_cost": 4785,
"name": "Healing Skill",
"on_use_mc_function": "example-mod:healing_skill_function"
}1
2
3
4
5
2
3
4
5
Die JSON-Dateien der Einträge, werden unter src/main/resources/data/example-mod/example-mod/magic_skills_registry/ gespeichert.
INFO
Das wiederholte example-mod/example-mod ist kein Fehler.
Das erste example-mod ist der Namensraum des Eintrags, der hinzugefügt wird. Das zweite example-mod leitet sich aus der ID der Registry selbst ab. Es ist normal, für beides dieselbe Mod-ID zu verwenden und dies erlaubt anderen Mods oder Datenpaketen unter ihrem eigenen Namensraum Einträge zu deiner Registrierung hinzufügen.
Beispielsweise könnte another-mod Elemente zu unserer magic_skills_registry hinzufügen wollen und dies würde er über Dateien im Verzeichnis src/main/resources/data/another-mod/example-mod/magic_skills_registry/ machen.
ID des Eintrag
Die ID des Eintrags ist ein eindeutiger Schlüssel für jeden Eintrag und kann nützlich sein, um auf einen spezifischen Eintrag in einer Registry zuzugreifen. Sie setzt sich aus dem Dateinamen und dem Registrierungsschlüssel zusammen. Da unsere JSON-Datei des Eintrags beispielsweise den Namen healing_skill.json hat, lautet die ID des Eintrags wie folgt:
java
ResourceKey<MagicSkillsRegistryEntry> HEALING_SKILL_ENTRY_ID = ResourceKey.create(ExampleModRegistries.MAGIC_SKILLS_SYNCED_REGISTRY_KEY, ExampleMod.id("healing_skill"));1
Auf die Registrydaten zugreifen
Dynamische Registries werden mit der Welt geladen und können über die Klasse RegistryAccess unter Verwendung deines Registryschlüssel abgerufen werden. Instanzen von RegistryAccess können aus vielen Klassen abgerufen werden, am häufigsten sind jedoch MinecraftServer, ServerLevel, ClientLevel, Entity und weitere.
WICHTIG
Beim Zugriff auf die RegistryAccess-Instanz aus einer reinen Client-Klasse wie beispielsweise ClientLevel stehen nur synchronisierte Registries zur Verfügung.
Die gesamte Registry abrufen
Der Zugriff auf Registries erfolgt über die Methode lookup von RegistryAccess, die einen Optional<Registry<T>> zurückgibt, wobei T der Typ der Registry ist.
java
Optional<Registry<MagicSkillsRegistryEntry>> registry = registryAccess.lookup(ExampleModRegistries.MAGIC_SKILLS_SYNCED_REGISTRY_KEY);1
Einen spezifischen Eintrag abrufen
Auf bestimmte Einträge kann über die Methode get von RegistryAccess zugegriffen werden, die einen Optional<Holder.Reference<T>> zurückgibt, wobei T der Typ der Registrierung ist.
java
ResourceKey<MagicSkillsRegistryEntry> HEALING_SKILL_ENTRY_ID = ResourceKey.create(ExampleModRegistries.MAGIC_SKILLS_SYNCED_REGISTRY_KEY, ExampleMod.id("healing_skill"));
Optional<Holder.Reference<MagicSkillsRegistryEntry>> entry = registryAccess.get(HEALING_SKILL_ENTRY_ID);
entry.ifPresent(magicSkillRef -> {
MagicSkillsRegistryEntry magicSkill = magicSkillRef.value();
// Other logic to reduce player's mana and running the function
serverPlayer.sendOverlayMessage(Component.literal("Used %s Magical Skill, Mana Reduced By %d".formatted(magicSkill.name(), magicSkill.manaCost())));
});1
2
3
4
5
6
7
2
3
4
5
6
7
Lies ID des Eintrags, um zu erfahren, wie du die HEALING_SKILL_ENTRY_ID erhältst.
In unserem Fall können wir diese Methode verwenden, um den Eintrag für die vom Benutzer auf dem Server verwendete magische Fähigkeit abzurufen und anschließend das Feld onUseMcFunction zu extrahieren, um die mcfunction auszuführen.
Über alle Einträge iterieren
Registryeinträge können für verschiedene Zwecke, wie beispielsweise das Ausfüllen der Benutzeroberfläche, durchlaufen werden. In unserem Fall können wir diese Methode verwenden, um eine Oberfläche wie folgt mit benutzerdefinierten Widgets zu füllen:
java
registry.ifPresent(reg -> {
int y = 50;
for (MagicSkillsRegistryEntry skill : reg) {
MagicSkillWidget widget = new MagicSkillWidget(skill, font, 40, y, 80, 20);
this.addRenderableWidget(widget);
y += 30;
}
});1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Eine von einer Registry befüllte benutzerdefinierte Oberfläche

Lerne mehr über die Erstellung von benutzerdefinierten Oberflächen und benutzerdefinierten Widgets.
Tags für benutzerdefinierte Registryeinträge
Tags sind ein Weg, mehrere Einträge zu gruppieren. Beispielsweise können wir Tags wie attack und defense erstellen, um ähnliche magische Fähigkeiten zu gruppieren.
Beispielsweise würde das Angriffstag unter data/example-mod/tags/example-mod/magic_skills_registry/attacking_skills.json definiert werden:
json
{
"values": [
"example-mod:blast_skill",
"example-mod:magic_missile_skill"
]
}1
2
3
4
5
6
2
3
4
5
6
Tags im Code verwenden
Erstelle einen Schlüssel für das Tag, um zu prüfen, ob Einträge in dem Tag vorhanden sind oder nicht.
java
public static final TagKey<MagicSkillsRegistryEntry> ATTACKING_SKILLS_TAG_KEY = TagKey.create(ExampleModRegistries.MAGIC_SKILLS_SYNCED_REGISTRY_KEY, ExampleMod.id("attacking_skills"));
public static <T> boolean isPresentInMyTag(RegistryAccess registryAccess, ResourceKey<T> entryId, TagKey<T> tagKey) {
return registryAccess.get(entryId).map(reference -> reference.is(tagKey)).orElse(false);
}1
2
3
4
5
2
3
4
5
Mit dieser Methode können wir überprüfen, ob es sich bei einer Fähigkeit um eine Angriffsfähigkeit handelt oder nicht.
java
boolean isAttackingSkill = isPresentInMyTag(registryAccess, entryId, ATTACKING_SKILLS_TAG_KEY);1

