Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fabric for Minecraft 1.21 #93

Merged
merged 2 commits into from
May 31, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions _posts/2024-05-31-121.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
layout: post
title: Fabric for Minecraft 1.21
ref: 121
---
Minecraft 1.21 is expected to be released on June 13, with some significant changes affecting mod makers.

It's been only a month and a half since we last released a blogpost. Yet, this update is nothing small - some changes are likely to affect most, if not all, mods.

As always, **we ask all players to be patient, and give mod developers time to update to this new version.** We ask everyone kindly not to pester them. **We also recommend all players to make a backup of their worlds.**

Here is a list of all major modder-facing changes in this version. Note that all code references are using Yarn mappings; modders using alternative mappings may need to use different names.

## Fabric changes
Developers should use Loom 1.6 (at the time of writing) to develop mods for Minecraft 1.21. Players should install the latest stable version of Fabric Loader (currently 0.15.11).

<!-- MAYBE: advertise Sandbox? -->

modmuss50 marked this conversation as resolved.
Show resolved Hide resolved
### New Fabric API changes
With the help of many contributors, Fabric API has received some new features since the last update blog post:

- Conventional Tags: Default English Translations for `c` namespaced tags (TelepathicGrunt)
- Data Generation: Support extending dynamic registries in datagen (SquidDev)
- Item API: Add API to modify default item components (modmuss50)
- Resource Conditions: Support loading a single resource condition instead of array (Apollo)
- Resource Conditions: Registry resource conditions (SquidDev)
- Removed Herobrine (Tiny Potato)

#### Tag translation
Mods are now strongly encouraged to provide an English translation for item tags it provides. For example, the translation for `test:potatoes` should be declared like this:

```json
{
"tag.item.test.potatoes": "Potatoes"
}
```

Although these are not used by the API itself, other mods (such as recipe viewers) rely on them for proper display.

### Breaking Changes
*Note: breaking changes related to vanilla changes are addressed separately below.*

One deprecated module was removed: `fabric-models-v0`.

In Item API, `EquipmentSlotProvider#getPreferredEquipmentSlot` is now passed a `LivingEntity`. Implementations are expected to check themselves whether the slot is available, using `entity.canUseSlot(EquipmentSlot)`. `FabricItem#getAttributeModifiers` was removed; use Default Components API instead.

Registry Sync now freezes the registry at an earlier point. This does not affect mods that register things in their `ModInitializer`s, but might affect more complex mods. Make sure to register everything during `ModInitializer`.

## Minecraft changes
This version brought many breaking changes, including data-driven Enchantments. Data pack structures also changed, using singular nouns (instead of plurals) in many places.

### Identifier
`Identifier` constructor is now `protected`. Use the static method `of` or `ofVanilla` to construct `Identifier`. Replacing `new Identifier` with `Identifier.of` using text editors or IDEs should be sufficient.

```diff=
modmuss50 marked this conversation as resolved.
Show resolved Hide resolved
- var id = new Identifier("example", "foo_bar");
- var id2 = new Identifier("test:single_argument");
- var creeper = new Identifier("minecraft", "creeper");
- @Nullable var customId = Identifier.of("namespace", unsanitizedInput);
+ var id = Identifier.of("example", "foo_bar");
+ var id2 = Identifier.of("test:single_argument");
+ var creeper = Identifier.ofVanilla("creeper"); // very slightly improved performance
+ @Nullable var customId = Identifier.tryParse("namespace", unsanitizedInput);
```

Pro tip: make a method to construct `Identifier` for your mod's namespace; this can be imported later using `import static` to shorten your code.

```java
public static Identifier id(String path) {
return Identifier.of("mod_namespace", path);
}
```

### Enchantments
Enchantments are now data-driven. This means that they are no longer hardcoded - instead, they're part of data packs. Consequentially, there are several important changes in how mods handle enchantments.

- Generally, enchantments either "do something" (like causing an explosion), or "modify a value" (like attack damage).
Example: Instead of checking if the player has Thorns and applying damage to the attacker, a mod should now call `EnchantmentHelper#onTargetDamaged` which should automatically handle this.
Example 2: Instead of checking the level of Knockback, a mod should now call `EnchantmentHelper#modifyKnockback` with the original knockback.
- Sometimes, a code needs to check if an item has certain enchantments. Because enchantments are no longer hard-coded, this is done using enchantment tags.
Example 3: Breaking a beehive with Silk Touch prevents bees from getting released. Previously, this was done by checking for the Silk Touch enchantment. Now, this is done by calling `EnchantmentHelper#hasAnyEnchantmentsIn(stack, EnchantmentTags#PREVENTS_BEE_SPAWNS_WHEN_MINING)`.

### Enchantment Code Structures
An `Enchantment` is, well, an enchantment. Because it is now defined by a dynamic registry, it is often handled as `RegistryEntry<Enchantment>`. These can be obtained from the dynamic registry, i.e. `world.getRegistryManager()` or the `registryLookup` passed to various methods.

An enchantment has effect components, which is a map of effect types to list of effects. Effect types are pre-defined keys that enchantments can define behaviors for. For example, an enchantment may want to add "multiply by 2" behavior for `DAMAGE` effect type, or "explode" behavior for `POST_ATTACK` effect type. These are defined in `EnchantmentEffectComponentTypes`.

The values of effect components are `EnchantmentEffectEntry`. This is a pair of the effect and an optional condition. `LootCondition` is reused for this purpose.

Enchantment effects can be categorized into three categories:

- `EnchantmentValueEffect`. When given a value, the effect calculates the modified value. Examples include Protection (which modifies `DAMAGE_PROTECTION`), Lure (`FISHING_LUCK_BONUS`), Unbreaking (`ITEM_DAMAGE`), and Multishot (`PROJECTILE_COUNT`).
- `EnchantmentEntityEffect`. This effect does something when triggered. Examples include Thorns and Channeling (`POST_ATTACK`) and Flame (`PROJECTILE_SPAWNED`).
- `AttributeEnchantmentEffect`, which adds an attribute to the entity. Unlike entity effects, attributes persist until the enchantment becomes inapplicable.

`EnchantmentLocationBasedEffect` is an interface implemented by `EnchantmentEntityEffect` and `AttributeEnchantmentEffect`. In addition, some enchantment effect types simply require effective values to be specified directly - like `CROSSBOW_CHARGING_SOUNDS`, which is a list of sounds. Some effects, like `PREVENT_ARMOR_CHANGE`, cannot be configured further.

### Using Vanilla Enchantments
`EnchantmentHelper` methods can be used to handle vanilla enchantments.

```java
// Example of modifying a value
float newDamage = EnchantmentHelper.getDamage(
world,
stack,
attackTarget,
damageSource,
baseDamage
);

// For values with no base value (such as Protection)
float protection = EnchantmentHelper.getProtectionAmount(
world,
user,
damageSource
);

// Entity-based effect
EnchantmentHelper.onTargetDamaged(
world,
attackTarget,
damageSource,
weapon
);

// Checking if an ItemStack is enchanted with certain enchantments
if (EnchantmentHelper.hasAnyEnchantmentsIn(
stack,
EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING
)) {
return;
}

// Checking for nonconfigurable effects
// e.g. checking for Curse of Binding
if (EnchantmentHelper.hasAnyEnchantmentsWith(
stack,
EnchantmentEffectComponentTypes.PREVENT_ARMOR_CHANGE
)) {
return;
}
```

### Adding a new Enchantment
Adding a new enchantment is now done by writing JSON files. You can use Data Generation to generate enchantments easily.

An enchantment can perform many tasks, like applying attributes, replacing blocks or summoning an entity. However, not all things are possible. To add a custom **effect type**, a new effect class must be created and its codec registered. **An effect type must exist in both the client and the server.**

Enchantment tags are used to specify exclusive enchantments (e.g. Fortune and Silk Touch) and where the enchantments can be obtained. Make sure to add your enchantments to those tags.

### Enchantment-related Fabric API changes
The following event callbacks now pass `RegistryEntry<Enchantment>` instead of `Enchantment`:

- `EnchantmentEvents.AllowEnchanting#allowEnchanting`
- `FabricItem#canBeEnchantedWith`
- `FabricItemStack#canBeEnchantedWith`

`EnchantmentContext` was reworked. Previously, a context was one of `RANDOM_ENCHANTMENT`, `ANVIL`, `ENCHANT_COMMAND`, or `LOOT_RANDOM_ENCHANTMENT`. To clarify its purpose, they are now one of `ACCEPTABLE` (e.g. anvils) or `PRIMARY` (e.g. enchanting tables). For example, a helmet can be enchanted with Thorns using anvils but not in enchanting tables, because only chestplates are `PRIMARY` in case of Thorns.

### Teleportation
Both intra-dimensional and cross-dimensional teleportation can now be performed using `Entity#teleportTo` (previously known as `moveToWorld`). `TeleportTarget` now contains the destination world and position.

`FabricDimensions` was removed, because its only API, `teleport`, is effectively superseded with `Entity#teleportTo`.

### Data Pack Paths
Data packs now use singular nouns in the following paths:

- Tag subdirectories such as `tags/blocks`, `tags/entity_types`, `tags/functions`. They are now `tags/block`, `tags/entity_type`, and `tags/function`, respectively. **The `tags` directory itself remains plural.**
- Advancements (previously `advancements`, now `advancement`).
- Functions (`functions` to `function`).
- Item modifiers/loot functions (`item_modifiers` to `item_modifier`).
- Loot tables (`loot_tables` to `loot_table`).
- Predicates/loot conditions (`predicates` to `predicate`).
- Recipes (`recipes` to `recipe`).
- Structures (`structures` to `structure`).

In Fabric API news, GameTest structure directories are now singular as well; instead of `gametest/structures`, they are now placed in `gametest/structure`.

### Rendering Changes
In the Fabric Rendering API, `HudRenderCallback` now passes `RenderTickCounter` instead of `tickDelta`. `tickDelta` can be obtained from the counter. `WorldRenderContext`'s `tickDelta` and `limitTime` methods are also replaced with `tickCounter`.

There are some other, significant internal refactors to rendering. For example, `BufferBuilder#next()` was removed, and the methods for beginning or ending the building process has changed:

```diff
- BufferBuilder builder = tessellator.getBuffer();
- builder.begin(...);
+ BufferBuilder builder = tessellator.begin(...);
- builder.vertex(...).texture(...).color(...).next();
+ builder.vertex(...).texture(...).color(...)
- tessellator.draw();
+ BufferRenderer.drawWithGlobalProgram(builder.end());
```

### Miscellaneous
- `FabricCodecProvider` now has a new constructor that takes `RegistryKey` instead of directory name. This can be used with, for example, item modifier providers. The old constructor can still be used for non-registered codecs.
Loading