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

Add Support for Custom Item Settings #1359

Open
wants to merge 20 commits into
base: 1.16
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9db6864
Adds support for custom item settings. Also fixes a bug with the test…
Haven-King Mar 11, 2021
ba39dc2
Checkstyle
Haven-King Mar 11, 2021
3edead5
Removes ExtraData, simplifying implementation.
Haven-King Mar 11, 2021
71ee36a
CustomItemSettings are now automagically stored on and retrieved from…
Haven-King Mar 11, 2021
cb62f41
CustomItemSettingType is now CustomItemSetting.
Haven-King Mar 11, 2021
9424fc4
Moves the custom setting logic from FabricItemInternals to a field in…
Haven-King Mar 11, 2021
3cb0310
Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/…
Haven-King Mar 11, 2021
9bb82cf
Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/…
Haven-King Mar 11, 2021
d6f7f4d
Checkstyle
Haven-King Mar 11, 2021
08a1d61
Separate CustomItemSetting into an interface and an implementation class
Haven-King Mar 12, 2021
74b5af6
Stupid licenses.
Haven-King Mar 12, 2021
b59ba05
Rename customSetting to custom for the sake of brevity.
Haven-King Mar 12, 2021
ff1c19f
Minor code cleanup.
Haven-King Mar 12, 2021
72b1255
Javadoc changes
Haven-King Mar 12, 2021
2629b83
- CustomItemSetting can now be extended, though doing so is discouraged
Haven-King Mar 26, 2021
f1d99bd
- Fix checkstyle
Haven-King Mar 26, 2021
9a7615f
I don't know how I accidentally deleted this.
Haven-King Mar 26, 2021
39d9ee4
- Minor changes.
Haven-King Mar 26, 2021
a845784
- Minor changes.
Haven-King Mar 26, 2021
c21d92e
- Remove implementation methods from the CustomItemSetting interface
Haven-King Mar 26, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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.function.Supplier;

import org.jetbrains.annotations.ApiStatus;

import net.minecraft.item.Item;

import net.fabricmc.fabric.impl.item.FabricItemInternals;

/**
* A type of setting that can be passed to item constructors.
* This feature can be used by mods to add non-standard settings
* to items in a way that is compatible with other mods that add
* settings to items.
*
* @param <T> the type of the setting to be attached
*/
public final class CustomItemSetting<T> {
private final Supplier<T> defaultValue;

private CustomItemSetting(Supplier<T> defaultValue) {
this.defaultValue = defaultValue;
}

// There are a lot of these for convenience sake.
public static <T> CustomItemSetting<T> of(Supplier<T> defaultValue) {
return new CustomItemSetting<>(defaultValue);
}

public static CustomItemSetting<Integer> of(int defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Long> of(long defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Float> of(float defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Double> of(double defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Boolean> of(boolean defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Byte> of(byte defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Short> of(short defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<Character> of(char defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

public static CustomItemSetting<String> of(String defaultValue) {
return new CustomItemSetting<>(() -> defaultValue);
}

@ApiStatus.Internal
public T getDefaultValue() {
return defaultValue.get();
}

/**
* Returns the current value of this setting for the given {@link Item}.
* Should only be called after or within item construction.
*
* @param item the item
* @return the current setting if present, the default setting if not
*/
public T getValue(Item item) {
return FabricItemInternals.getSetting(item, this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

package net.fabricmc.fabric.api.item.v1;

import java.util.Map;
import java.util.WeakHashMap;

import org.jetbrains.annotations.ApiStatus;

import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
Expand All @@ -32,14 +37,16 @@
* {@code new FabricItemSettings()}.
*/
public class FabricItemSettings extends Item.Settings {
private final Map<CustomItemSetting<?>, Object> customSettings = new WeakHashMap<>();

/**
* 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);
this.customSettings.put(FabricItemInternals.EQUIPMENT_SLOT_PROVIDER, equipmentSlotProvider);
return this;
}

Expand All @@ -49,10 +56,27 @@ public FabricItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvi
* @see CustomDamageHandler
*/
public FabricItemSettings customDamage(CustomDamageHandler handler) {
FabricItemInternals.computeExtraData(this).customDamage(handler);
this.customSettings.put(FabricItemInternals.CUSTOM_DAMAGE_HANDLER, handler);
return this;
}

/**
* Sets a custom setting of the item.

* @param setting the unique type for this setting
* @param value the object containing the setting itself
* @return this builder
*/
public <T> FabricItemSettings customSetting(CustomItemSetting<T> setting, T value) {
this.customSettings.put(setting, value);
return this;
}

@ApiStatus.Internal
public Map<CustomItemSetting<?>, ?> getCustomSettings() {
return this.customSettings;
}

// Overrides of vanilla methods

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,30 @@

package net.fabricmc.fabric.impl.item;

import java.util.WeakHashMap;
import java.util.Map;

import net.minecraft.item.Item;

import net.fabricmc.fabric.api.item.v1.CustomItemSetting;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;

public final class FabricItemInternals {
private static final WeakHashMap<Item.Settings, ExtraData> extraData = new WeakHashMap<>();
public static final CustomItemSetting<EquipmentSlotProvider> EQUIPMENT_SLOT_PROVIDER = CustomItemSetting.of(() -> null);
public static final CustomItemSetting<CustomDamageHandler> CUSTOM_DAMAGE_HANDLER = CustomItemSetting.of(() -> null);

private FabricItemInternals() {
}

public static ExtraData computeExtraData(Item.Settings settings) {
return extraData.computeIfAbsent(settings, s -> new ExtraData());
public static <T> T getSetting(Item item, CustomItemSetting<T> setting) {
return ((ItemExtensions) item).fabric_getCustomItemSetting(setting);
}

public static void onBuild(Item.Settings settings, Item item) {
ExtraData data = extraData.get(settings);

if (data != null) {
((ItemExtensions) item).fabric_setEquipmentSlotProvider(data.equipmentSlotProvider);
((ItemExtensions) item).fabric_setCustomDamageHandler(data.customDamageHandler);
}
}

public static final class ExtraData {
private /* @Nullable */ EquipmentSlotProvider equipmentSlotProvider;
private /* @Nullable */ CustomDamageHandler customDamageHandler;

public void equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
this.equipmentSlotProvider = equipmentSlotProvider;
}

public void customDamage(CustomDamageHandler handler) {
this.customDamageHandler = handler;
public static void onBuild(Item.Settings settings, ItemExtensions item) {
if (settings instanceof FabricItemSettings) {
Map<CustomItemSetting<?>, Object> customItemSettings = item.fabric_getCustomItemSettings();
customItemSettings.putAll(((FabricItemSettings) settings).getCustomSettings());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package net.fabricmc.fabric.impl.item;

import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import java.util.Map;

import org.jetbrains.annotations.NotNull;

import net.fabricmc.fabric.api.item.v1.CustomItemSetting;

public interface ItemExtensions {
/* @Nullable */ EquipmentSlotProvider fabric_getEquipmentSlotProvider();
void fabric_setEquipmentSlotProvider(EquipmentSlotProvider equipmentSlotProvider);
/* @Nullable */ CustomDamageHandler fabric_getCustomDamageHandler();
void fabric_setCustomDamageHandler(CustomDamageHandler handler);
@NotNull Map<CustomItemSetting<?>, Object> fabric_getCustomItemSettings();
<T> T fabric_getCustomItemSetting(CustomItemSetting<T> type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package net.fabricmc.fabric.mixin.item;

import java.util.HashMap;
import java.util.Map;

import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -24,41 +28,28 @@

import net.minecraft.item.Item;

import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
import net.fabricmc.fabric.impl.item.ItemExtensions;
import net.fabricmc.fabric.api.item.v1.CustomItemSetting;

@Mixin(Item.class)
abstract class ItemMixin implements ItemExtensions {
@Unique
private EquipmentSlotProvider equipmentSlotProvider;

class ItemMixin implements ItemExtensions {
@Unique
private CustomDamageHandler customDamageHandler;
private final HashMap<CustomItemSetting<?>, Object> customItemSettings = new HashMap<>();

@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;
FabricItemInternals.onBuild(settings, this);
}

@Override
public CustomDamageHandler fabric_getCustomDamageHandler() {
return customDamageHandler;
public @NotNull Map<CustomItemSetting<?>, Object> fabric_getCustomItemSettings() {
return this.customItemSettings;
}

@Override
public void fabric_setCustomDamageHandler(CustomDamageHandler handler) {
this.customDamageHandler = handler;
@SuppressWarnings("unchecked")
public <T> @NotNull T fabric_getCustomItemSetting(CustomItemSetting<T> type) {
return (T) this.customItemSettings.computeIfAbsent(type, CustomItemSetting::getDefaultValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

import net.fabricmc.fabric.impl.item.FabricItemInternals;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.impl.item.ItemExtensions;

@Mixin(ItemStack.class)
public abstract class ItemStackMixin {
Expand All @@ -51,7 +51,7 @@ private void saveDamager(int amount, LivingEntity entity, Consumer<LivingEntity>

@ModifyArg(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILjava/util/Random;Lnet/minecraft/server/network/ServerPlayerEntity;)Z"), index = 0)
private int hookDamage(int amount) {
CustomDamageHandler handler = ((ItemExtensions) getItem()).fabric_getCustomDamageHandler();
CustomDamageHandler handler = FabricItemInternals.CUSTOM_DAMAGE_HANDLER.getValue(this.getItem());

if (handler != null) {
return handler.damage((ItemStack) (Object) this, amount, fabric_damagingEntity, fabric_breakCallback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

import net.fabricmc.fabric.impl.item.FabricItemInternals;
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();
EquipmentSlotProvider equipmentSlotProvider = FabricItemInternals.EQUIPMENT_SLOT_PROVIDER.getValue(item);

if (equipmentSlotProvider != null) {
info.setReturnValue(equipmentSlotProvider.getPreferredEquipmentSlot(stack));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@

import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.item.v1.CustomItemSetting;

public class FabricItemSettingsTests implements ModInitializer {
public static final CustomItemSetting<String> CUSTOM_DATA_TEST = CustomItemSetting.of(() -> null);

@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);

Item testItem2 = new Item(new FabricItemSettings().group(ItemGroup.MISC).customSetting(CUSTOM_DATA_TEST, "Look at me! I have a custom setting!"));
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "test_item2"), testItem2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.test.item.FabricItemSettingsTests;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;

@Environment(EnvType.CLIENT)
Expand All @@ -31,6 +32,12 @@ public void onInitializeClient() {
// Adds a tooltip to all items so testing can be verified easily.
ItemTooltipCallback.EVENT.register((stack, context, lines) -> {
lines.add(new LiteralText("Fancy Tooltips").formatted(Formatting.LIGHT_PURPLE));

String string = FabricItemSettingsTests.CUSTOM_DATA_TEST.getValue(stack.getItem());

if (string != null) {
lines.add(new LiteralText(string));
}
});
}
}
7 changes: 3 additions & 4 deletions fabric-item-api-v1/src/testmod/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.item.FabricItemSettingsTests"
"net.fabricmc.fabric.test.item.FabricItemSettingsTests",
"net.fabricmc.fabric.test.item.CustomDamageTest"

],
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
],
"main": [
"net.fabricmc.fabric.test.item.CustomDamageTest"
]
}
}