Add FabricItemSettings and an API for custom item equipment slots (#956)

* Add FabricItemSettings and an API for custom item equipment slots

Closes #952.

* Bump module version

* Rename duck method to have fabric_ prefix and add Unique to mixin field

* Switch to FabricItemInternals + ExtraData like in FabricBlockSettings
This commit is contained in:
Juuxel 2020-08-21 19:21:09 +03:00 committed by modmuss50
parent db9ee7acda
commit eb1e5e8520
10 changed files with 348 additions and 1 deletions

View file

@ -1,5 +1,5 @@
archivesBaseName = "fabric-item-api-v1"
version = getSubprojectVersion(project, "1.0.0")
version = getSubprojectVersion(project, "1.1.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')

View file

@ -0,0 +1,44 @@
/*
* 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 net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
/**
* A provider for the preferred equipment slot of an item.
* This can be used to give non-armor items, such as blocks,
* an armor slot that they can go in.
*
* <p>Equipment slot providers can be set with {@link FabricItemSettings#equipmentSlot(EquipmentSlotProvider)}.
*
* <p>Note that items extending {@link net.minecraft.item.ArmorItem} don't need to use this
* as there's {@link net.minecraft.item.ArmorItem#getSlotType()}.
*/
@FunctionalInterface
public interface EquipmentSlotProvider {
/**
* Gets the preferred equipment slot for an item stack.
*
* <p>If there is no preferred armor equipment slot for the stack,
* {@link EquipmentSlot#MAINHAND} can be returned.
*
* @param stack the item stack
* @return the preferred equipment slot
*/
EquipmentSlot getPreferredEquipmentSlot(ItemStack stack);
}

View file

@ -0,0 +1,94 @@
/*
* 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 net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.Rarity;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
/**
* Fabric's version of Item.Settings. Adds additional methods and hooks
* not found in the original class.
*
* <p>To use it, simply replace {@code new Item.Settings()} with
* {@code new FabricItemSettings()}.
*/
public class FabricItemSettings extends Item.Settings {
/**
* Sets the equipment slot provider of the item.
*
* @param equipmentSlotProvider the equipment slot provider
* @return this builder
*/
public FabricItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
FabricItemInternals.computeExtraData(this).equipmentSlot(equipmentSlotProvider);
return this;
}
// Overrides of vanilla methods
@Override
public FabricItemSettings food(FoodComponent foodComponent) {
super.food(foodComponent);
return this;
}
@Override
public FabricItemSettings maxCount(int maxCount) {
super.maxCount(maxCount);
return this;
}
@Override
public FabricItemSettings maxDamageIfAbsent(int maxDamage) {
super.maxDamageIfAbsent(maxDamage);
return this;
}
@Override
public FabricItemSettings maxDamage(int maxDamage) {
super.maxDamage(maxDamage);
return this;
}
@Override
public FabricItemSettings recipeRemainder(Item recipeRemainder) {
super.recipeRemainder(recipeRemainder);
return this;
}
@Override
public FabricItemSettings group(ItemGroup group) {
super.group(group);
return this;
}
@Override
public FabricItemSettings rarity(Rarity rarity) {
super.rarity(rarity);
return this;
}
@Override
public FabricItemSettings fireproof() {
super.fireproof();
return this;
}
}

View file

@ -0,0 +1,50 @@
/*
* 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.WeakHashMap;
import net.minecraft.item.Item;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
public final class FabricItemInternals {
private static final WeakHashMap<Item.Settings, ExtraData> extraData = new WeakHashMap<>();
private FabricItemInternals() {
}
public static ExtraData computeExtraData(Item.Settings settings) {
return extraData.computeIfAbsent(settings, s -> new ExtraData());
}
public static void onBuild(Item.Settings settings, Item item) {
ExtraData data = extraData.get(settings);
if (data != null) {
((ItemExtensions) item).fabric_setEquipmentSlotProvider(data.equipmentSlotProvider);
}
}
public static final class ExtraData {
private /* @Nullable */ EquipmentSlotProvider equipmentSlotProvider;
public void equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
this.equipmentSlotProvider = equipmentSlotProvider;
}
}
}

View file

@ -0,0 +1,24 @@
/*
* 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 net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
public interface ItemExtensions {
EquipmentSlotProvider fabric_getEquipmentSlotProvider();
void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider);
}

View file

@ -0,0 +1,50 @@
/*
* 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.Unique;
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.item.Item;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
import net.fabricmc.fabric.impl.item.ItemExtensions;
@Mixin(Item.class)
abstract class ItemMixin implements ItemExtensions {
@Unique
private EquipmentSlotProvider equipmentSlotProvider;
@Inject(method = "<init>", at = @At("RETURN"))
private void onConstruct(Item.Settings settings, CallbackInfo info) {
FabricItemInternals.onBuild(settings, (Item) (Object) this);
}
@Override
public EquipmentSlotProvider fabric_getEquipmentSlotProvider() {
return equipmentSlotProvider;
}
@Override
public void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider) {
this.equipmentSlotProvider = equipmentSlotProvider;
}
}

View file

@ -0,0 +1,43 @@
/*
* 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.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.impl.item.ItemExtensions;
@Mixin(MobEntity.class)
abstract class MobEntityMixin {
@Inject(method = "getPreferredEquipmentSlot", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/item/ItemStack;getItem()Lnet/minecraft/item/Item;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
private static void onGetPreferredEquipmentSlot(ItemStack stack, CallbackInfoReturnable<EquipmentSlot> info, Item item) {
EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) item).fabric_getEquipmentSlotProvider();
if (equipmentSlotProvider != null) {
info.setReturnValue(equipmentSlotProvider.getPreferredEquipmentSlot(stack));
}
}
}

View file

@ -2,6 +2,10 @@
"required": true,
"package": "net.fabricmc.fabric.mixin.item",
"compatibilityLevel": "JAVA_8",
"mixins": [
"ItemMixin",
"MobEntityMixin"
],
"client": [
"client.ItemStackMixin"
],

View file

@ -0,0 +1,35 @@
/*
* 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 net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
public class FabricItemSettingsTests implements ModInitializer {
@Override
public void onInitialize() {
// Registers an item with a custom equipment slot.
Item testItem = new Item(new FabricItemSettings().group(ItemGroup.MISC).equipmentSlot(stack -> EquipmentSlot.CHEST));
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "test_item"), testItem);
}
}

View file

@ -9,6 +9,9 @@
"fabric-item-api-v1": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.item.FabricItemSettingsTests"
],
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
]