mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
Add API to modify default item components (#3728)
* Add API to modify default item components * Add test for removal * Some review feedback * API design changes * Review feedback * Add overload that takes a Collection<Item>
This commit is contained in:
parent
c0e5481f61
commit
5bcea88aba
8 changed files with 344 additions and 1 deletions
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.item.v1;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.component.ComponentMap;
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Events to modify the default {@link ComponentMap} of items.
|
||||
*/
|
||||
public final class DefaultItemComponentEvents {
|
||||
/**
|
||||
* Event used to add or remove data components to known items.
|
||||
*/
|
||||
public static final Event<ModifyCallback> MODIFY = EventFactory.createArrayBacked(ModifyCallback.class, listeners -> context -> {
|
||||
for (ModifyCallback listener : listeners) {
|
||||
listener.modify(context);
|
||||
}
|
||||
});
|
||||
|
||||
private DefaultItemComponentEvents() {
|
||||
}
|
||||
|
||||
public interface ModifyContext {
|
||||
/**
|
||||
* Modify the default data components of the specified item.
|
||||
*
|
||||
* @param itemPredicate A predicate to match items to modify
|
||||
* @param builderConsumer A consumer that provides a {@link ComponentMap.Builder} to modify the item's components.
|
||||
*/
|
||||
void modify(Predicate<Item> itemPredicate, BiConsumer<ComponentMap.Builder, Item> builderConsumer);
|
||||
|
||||
/**
|
||||
* Modify the default data components of the specified item.
|
||||
*
|
||||
* @param item The item to modify
|
||||
* @param builderConsumer A consumer that provides a {@link ComponentMap.Builder} to modify the item's components.
|
||||
*/
|
||||
default void modify(Item item, Consumer<ComponentMap.Builder> builderConsumer) {
|
||||
modify(Predicate.isEqual(item), (builder, _item) -> builderConsumer.accept(builder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the default data components of the specified items.
|
||||
* @param items The items to modify
|
||||
* @param builderConsumer A consumer that provides a {@link ComponentMap.Builder} to modify the item's components.
|
||||
*/
|
||||
default void modify(Collection<Item> items, BiConsumer<ComponentMap.Builder, Item> builderConsumer) {
|
||||
modify(items::contains, builderConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ModifyCallback {
|
||||
/**
|
||||
* Modify the default data components of items using the provided {@link ModifyContext} instance.
|
||||
*
|
||||
* @param context The context to modify items
|
||||
*/
|
||||
void modify(ModifyContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.item;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.component.ComponentMap;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.DefaultItemComponentEvents;
|
||||
import net.fabricmc.fabric.mixin.item.ItemAccessor;
|
||||
|
||||
public class DefaultItemComponentImpl {
|
||||
public static void modifyItemComponents() {
|
||||
DefaultItemComponentEvents.MODIFY.invoker().modify(ModifyContextImpl.INSTANCE);
|
||||
}
|
||||
|
||||
static class ModifyContextImpl implements DefaultItemComponentEvents.ModifyContext {
|
||||
private static final ModifyContextImpl INSTANCE = new ModifyContextImpl();
|
||||
|
||||
private ModifyContextImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modify(Predicate<Item> itemPredicate, BiConsumer<ComponentMap.Builder, Item> builderConsumer) {
|
||||
for (Item item : Registries.ITEM) {
|
||||
if (itemPredicate.test(item)) {
|
||||
ComponentMap.Builder builder = ComponentMap.builder().addAll(item.getComponents());
|
||||
builderConsumer.accept(builder, item);
|
||||
((ItemAccessor) item).setComponents(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.item;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.component.ComponentMap;
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
@Mixin(Item.class)
|
||||
public interface ItemAccessor {
|
||||
@Accessor
|
||||
@Mutable
|
||||
void setComponents(ComponentMap components);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.item;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
import net.fabricmc.fabric.impl.item.DefaultItemComponentImpl;
|
||||
|
||||
@Mixin(Registries.class)
|
||||
public abstract class RegistriesMixin {
|
||||
@Inject(method = "freezeRegistries", at = @At("HEAD"))
|
||||
private static void modifyDefaultItemComponents(CallbackInfo ci) {
|
||||
DefaultItemComponentImpl.modifyItemComponents();
|
||||
}
|
||||
}
|
|
@ -9,11 +9,13 @@
|
|||
"EnchantCommandMixin",
|
||||
"EnchantmentHelperMixin",
|
||||
"EnchantRandomlyLootFunctionMixin",
|
||||
"ItemAccessor",
|
||||
"ItemMixin",
|
||||
"ItemSettingsMixin",
|
||||
"ItemStackMixin",
|
||||
"LivingEntityMixin",
|
||||
"RecipeMixin"
|
||||
"RecipeMixin",
|
||||
"RegistriesMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.FireworkExplosionComponent;
|
||||
import net.minecraft.component.type.FireworksComponent;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.item.v1.DefaultItemComponentEvents;
|
||||
|
||||
public class DefaultItemComponentTest implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Identifier latePhase = new Identifier("fabric-item-api-v1-testmod", "late");
|
||||
DefaultItemComponentEvents.MODIFY.addPhaseOrdering(Event.DEFAULT_PHASE, latePhase);
|
||||
|
||||
DefaultItemComponentEvents.MODIFY.register(context -> {
|
||||
context.modify(Items.GOLD_INGOT, builder -> {
|
||||
builder.add(DataComponentTypes.ITEM_NAME, Text.literal("Fool's Gold").formatted(Formatting.GOLD));
|
||||
});
|
||||
context.modify(Items.GOLD_NUGGET, builder -> {
|
||||
builder.add(DataComponentTypes.FIREWORKS, new FireworksComponent(1, List.of(
|
||||
new FireworkExplosionComponent(FireworkExplosionComponent.Type.STAR, IntList.of(0x32a852), IntList.of(0x32a852), true, true)
|
||||
)));
|
||||
});
|
||||
context.modify(Items.BEEF, builder -> {
|
||||
// Remove the food component from beef
|
||||
builder.add(DataComponentTypes.FOOD, null);
|
||||
});
|
||||
});
|
||||
|
||||
// Make all fireworks glint
|
||||
DefaultItemComponentEvents.MODIFY.register(latePhase, context -> {
|
||||
context.modify(item -> item.getComponents().contains(DataComponentTypes.FIREWORKS), (builder, item) -> {
|
||||
builder.add(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.FireworksComponent;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.GameTestException;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
|
||||
public class DefaultItemComponentGameTest implements FabricGameTest {
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
public void modify(TestContext context) {
|
||||
Consumer<Text> checkText = text -> {
|
||||
if (text == null) {
|
||||
throw new GameTestException("Item name component not found on gold ingot");
|
||||
}
|
||||
|
||||
if (!"Fool's Gold".equals(text.getString())) {
|
||||
throw new GameTestException("Item name component on gold ingot is not set");
|
||||
}
|
||||
};
|
||||
|
||||
Text text = Items.GOLD_INGOT.getComponents().get(DataComponentTypes.ITEM_NAME);
|
||||
checkText.accept(text);
|
||||
|
||||
text = new ItemStack(Items.GOLD_INGOT).getComponents().get(DataComponentTypes.ITEM_NAME);
|
||||
checkText.accept(text);
|
||||
|
||||
boolean isBeefFood = Items.BEEF.getComponents().contains(DataComponentTypes.FOOD);
|
||||
|
||||
if (isBeefFood) {
|
||||
throw new GameTestException("Food component not removed from beef");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
public void afterModify(TestContext context) {
|
||||
FireworksComponent fireworksComponent = Items.GOLD_NUGGET.getComponents().get(DataComponentTypes.FIREWORKS);
|
||||
|
||||
if (fireworksComponent == null) {
|
||||
throw new GameTestException("Fireworks component not found on gold nugget");
|
||||
}
|
||||
|
||||
Boolean enchantGlint = Items.GOLD_NUGGET.getComponents().get(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
|
||||
if (enchantGlint != Boolean.TRUE) {
|
||||
throw new GameTestException("Enchantment glint override not set on gold nugget");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.item.CustomDamageTest",
|
||||
"net.fabricmc.fabric.test.item.DefaultItemComponentTest",
|
||||
"net.fabricmc.fabric.test.item.ItemUpdateAnimationTest",
|
||||
"net.fabricmc.fabric.test.item.ArmorKnockbackResistanceTest"
|
||||
],
|
||||
|
@ -19,6 +20,7 @@
|
|||
],
|
||||
"fabric-gametest" : [
|
||||
"net.fabricmc.fabric.test.item.gametest.BrewingStandGameTest",
|
||||
"net.fabricmc.fabric.test.item.gametest.DefaultItemComponentGameTest",
|
||||
"net.fabricmc.fabric.test.item.gametest.FurnaceGameTest",
|
||||
"net.fabricmc.fabric.test.item.gametest.RecipeGameTest"
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue