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 all 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,86 @@
/*
* 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 net.minecraft.item.Item;

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

/**
* 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.
*
* <p>Values of this setting can be retrieved from an item using {@link CustomItemSetting#get(Item)},
* and users that wish to expose a custom setting for use in other mods should do so by exposing
* the CustomItemSetting instance.
*
* <h3>Usage Example</h3>
* <pre>{@code
* // Create the setting instance. You can think of this as the "setting key".
* public static final CustomItemSetting<String> CUSTOM_TOOLTIP = CustomItemSetting.create(() -> null);
*
* @Override
* public void onInitializeClient() {
* ItemTooltipCallback.EVENT.register((stack, context, lines) -> {
* // Gets the setting from the specified item.
* String tooltip = CUSTOM_TOOLTIP.getValue(stack.getItem());
*
* if (tooltip != null) {
* lines.add(new LiteralText(tooltip));
* }
* }
* }}</pre>
*
* <p>You should not implement this interface.
*
* <p>Use {@link CustomItemSetting#create(Supplier)} to retrieve an instance of Fabric API's implementation.
*
* @param <T> the type of the setting to be attached
*/
public interface CustomItemSetting<T> {
/**
* Returns the current value of this setting for the given {@link Item}.
*
* @param item the item
* @return the current setting if present, the default setting if not
*/
T get(Item item);

/**
* Creates a new CustomItemSetting with the given default value.
*
* @param defaultValue the value all items that do not explicitly set this setting will have.
* @return a new CustomItemSetting
*/
static <T> CustomItemSetting<T> create(Supplier<T> defaultValue) {
return new CustomItemSettingImpl<>(defaultValue);
}

/**
* Creates a new CustomItemSetting with the given default value.
*
* @param defaultValue the value all items that do not explicitly set this setting will have.
* @return a new CustomItemSetting
*/
static <T> CustomItemSetting<T> create(T defaultValue) {
return new CustomItemSettingImpl<>(() -> defaultValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import net.minecraft.item.ItemStack;
import net.minecraft.util.Rarity;

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

/**
* Fabric's version of Item.Settings. Adds additional methods and hooks
Expand All @@ -39,8 +39,7 @@ public class FabricItemSettings extends Item.Settings {
* @return this builder
*/
public FabricItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
FabricItemInternals.computeExtraData(this).equipmentSlot(equipmentSlotProvider);
return this;
return this.custom(CustomItemSettingImpl.EQUIPMENT_SLOT_PROVIDER, equipmentSlotProvider);
}

/**
Expand All @@ -49,7 +48,22 @@ public FabricItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvi
* @see CustomDamageHandler
*/
public FabricItemSettings customDamage(CustomDamageHandler handler) {
FabricItemInternals.computeExtraData(this).customDamage(handler);
return this.custom(CustomItemSettingImpl.CUSTOM_DAMAGE_HANDLER, handler);
}

/**
* 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 custom(CustomItemSetting<T> setting, T value) {
if (!(setting instanceof CustomItemSettingImpl)) {
throw new UnsupportedOperationException("CustomItemSetting should not be ");
}

((CustomItemSettingImpl<T>) setting).set(this, value);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Supplier;

import net.minecraft.item.Item;

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

public class CustomItemSettingImpl<T> implements CustomItemSetting<T> {
public static final CustomItemSetting<EquipmentSlotProvider> EQUIPMENT_SLOT_PROVIDER = CustomItemSetting.create(() -> null);
public static final CustomItemSetting<CustomDamageHandler> CUSTOM_DAMAGE_HANDLER = CustomItemSetting.create(() -> null);

private static final Map<Item.Settings, Collection<CustomItemSettingImpl<?>>> CUSTOM_SETTINGS = new WeakHashMap<>();

private final Map<Item.Settings, T> customSettings = new WeakHashMap<>();
private final Map<Item, T> customItemSettings = new HashMap<>();
private final Supplier<T> defaultValue;

public CustomItemSettingImpl(Supplier<T> defaultValue) {
Objects.requireNonNull(defaultValue);

this.defaultValue = defaultValue;
}

@Override
public T get(Item item) {
Objects.requireNonNull(item);

return this.customItemSettings.computeIfAbsent(item, i -> this.defaultValue.get());
}

public void set(Item.Settings settings, T value) {
Objects.requireNonNull(settings);

this.customSettings.put(settings, value);
CUSTOM_SETTINGS.computeIfAbsent(settings, s -> new HashSet<>()).add(this);
}

public void apply(Item.Settings settings, Item item) {
Objects.requireNonNull(settings);

this.customItemSettings.put(item, this.customSettings.getOrDefault(settings, this.defaultValue.get()));
}

// Because item settings are reusable, it is possible that the same item settings object will be applied
// to multiple items.
public static void onBuild(Item.Settings settings, Item item) {
for (CustomItemSettingImpl<?> setting : CUSTOM_SETTINGS.getOrDefault(settings, Collections.emptyList())) {
setting.apply(settings, item);
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,18 @@
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.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.impl.item.CustomItemSettingImpl;

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

@Unique
private CustomDamageHandler customDamageHandler;

class ItemMixin {
@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;
}

@Override
public CustomDamageHandler fabric_getCustomDamageHandler() {
return customDamageHandler;
}

@Override
public void fabric_setCustomDamageHandler(CustomDamageHandler handler) {
this.customDamageHandler = handler;
CustomItemSettingImpl.onBuild(settings, (Item) (Object) this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import net.minecraft.item.ItemStack;

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

@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 = CustomItemSettingImpl.CUSTOM_DAMAGE_HANDLER.get(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.CustomItemSettingImpl;
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 = CustomItemSettingImpl.EQUIPMENT_SLOT_PROVIDER.get(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.create(() -> 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).custom(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);
}
}
Loading