Update Dynamic Attribute Tool modifier application to fix #1353 (#1355)

* dynamic tool attributes v2, part 1

* move duck interface to impl package

* add licenses

* fix mixin build failure on a dedicated server

* remove unused shadow of getItem()

* add a simple user-context-based attribute tool to the test item suite

* add clarifying comments in DynamicAttributeTool jdoc regarding parameter reliability + extended docs on getDynamicModifiers for attribute freshing

* player -> user because context is not always a player

* add license to TestNullableItem
This commit is contained in:
Draylar 2021-04-26 14:38:36 -05:00 committed by Player
parent b90688755c
commit f5d458fc56
13 changed files with 385 additions and 192 deletions

View file

@ -1,9 +1,10 @@
archivesBaseName = "fabric-tool-attribute-api-v1"
version = getSubprojectVersion(project, "1.2.9")
version = getSubprojectVersion(project, "1.2.10")
dependencies {
testmodCompile project(path: ':fabric-object-builder-api-v1', configuration: 'dev')
testmodCompile project(path: ':fabric-lifecycle-events-v1', configuration: 'dev')
testmodCompile project(path: ':fabric-resource-loader-v0', configuration: 'dev')
}
moduleDependencies(project, [

View file

@ -31,6 +31,9 @@ import net.minecraft.tag.Tag;
/**
* Interface for adding various tool attributes to items.
*
* <p> Functions in this interface will provide user context if it is available.
* These context parameters are provided on a best-effort basis, and implementations should not fail hard if they are absent.</p>
*/
public interface DynamicAttributeTool {
Multimap<EntityAttribute, EntityAttributeModifier> EMPTY = ImmutableSetMultimap.of();
@ -107,11 +110,14 @@ public interface DynamicAttributeTool {
/**
* Add modifiers for any {@link net.minecraft.entity.attribute.EntityAttributes} your item should give when equipped, based on the stack.
*
* <p>Appends to either attribute modifier NBT or the result from {@link net.minecraft.item.Item#getAttributeModifiers(EquipmentSlot)}.</p>
* <p>Appends to either attribute modifier NBT or the result from {@link net.minecraft.item.Item#getAttributeModifiers(EquipmentSlot)}.
* The attributes returned from this method will only be applied to an entity when the {@link ItemStack} providing the attributes is modified, or the player re-selects the stack in their hotbar.
* If your attribute relies on data from outside the stack, such as the user's age, you will need to modify the stack in some way to re-assign attributes to the entity.
* A fix for this may be provided in the future.</p>
*
* @param slot The equipment slot this item is equipped in.
* @param stack The stack that's equipped.
* @param user The current user of the tool, or none if there isn't any
* @param user The current user of the tool, if available.
* @return The dynamic modifiers to add on top of other modifiers on this stack. If none, return {@link #EMPTY}.
*/
default Multimap<EntityAttribute, EntityAttributeModifier> getDynamicModifiers(EquipmentSlot slot, ItemStack stack, @Nullable LivingEntity user) {

View file

@ -1,118 +0,0 @@
/*
* 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.tool.attribute;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
public final class AttributeManager {
private AttributeManager() { }
/**
* Merge two multimaps of EntityAttributeModifiers, combining their modifiers to prevent duplicate entries in a tooltip.
*
* @param left The first of the two multimaps to merge.
* @param right The second of the two multimaps to merge.
* @return The merged form of the two.
*/
public static Multimap<EntityAttribute, EntityAttributeModifier> mergeAttributes(Multimap<EntityAttribute, EntityAttributeModifier> left, Multimap<EntityAttribute, EntityAttributeModifier> right) {
if (left.isEmpty()) return right;
if (right.isEmpty()) return left;
Multimap<EntityAttribute, EntityAttributeModifier> ret = HashMultimap.create();
Set<EntityAttribute> allKeys = new HashSet<>();
allKeys.addAll(left.keySet());
allKeys.addAll(right.keySet());
for (EntityAttribute key : allKeys) {
Collection<EntityAttributeModifier> leftModifiers = left.get(key);
Collection<EntityAttributeModifier> rightModifiers = right.get(key);
if (leftModifiers.isEmpty()) {
ret.putAll(key, rightModifiers);
} else if (rightModifiers.isEmpty()) {
ret.putAll(key, leftModifiers);
} else {
Collection<EntityAttributeModifier> modifiers = new ArrayList<>(leftModifiers.size() + rightModifiers.size());
modifiers.addAll(leftModifiers);
modifiers.addAll(rightModifiers);
EntityAttributeModifier lastAddMod = null;
EntityAttributeModifier lastMultBaseMod = null;
EntityAttributeModifier lastMultTotalMod = null;
int addCount = 0;
int multBaseCount = 0;
int multTotalCount = 0;
double add = 0;
double multBase = 1;
double multTotal = 1;
for (EntityAttributeModifier mod : modifiers) {
double amount = mod.getValue();
if (amount == 0) continue;
switch (mod.getOperation()) {
case ADDITION:
lastAddMod = mod;
addCount++;
add += amount;
break;
case MULTIPLY_BASE:
lastMultBaseMod = mod;
multBaseCount++;
multBase += amount;
break;
case MULTIPLY_TOTAL:
lastMultTotalMod = mod;
multTotalCount++;
multTotal *= 1 + amount;
break;
default:
break;
}
}
if (addCount == 1) {
ret.put(key, lastAddMod);
} else if (addCount > 0) {
ret.put(key, new EntityAttributeModifier("merged add", add, EntityAttributeModifier.Operation.ADDITION));
}
if (multBaseCount == 1) {
ret.put(key, lastMultBaseMod);
} else if (multBaseCount > 0) {
ret.put(key, new EntityAttributeModifier("merged multiply-base", multBase, EntityAttributeModifier.Operation.MULTIPLY_BASE));
}
if (multTotalCount == 1) {
ret.put(key, lastMultTotalMod);
} else if (multTotalCount > 0) {
ret.put(key, new EntityAttributeModifier("merged multiply-total", multTotal, EntityAttributeModifier.Operation.MULTIPLY_TOTAL));
}
}
}
return ret;
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.tool.attribute;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.LivingEntity;
public interface ItemStackContext {
void fabricToolAttributes_setContext(@Nullable LivingEntity contextEntity);
}

View file

@ -0,0 +1,74 @@
/*
* 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.tool.attribute;
import java.util.Map;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
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.Redirect;
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.LivingEntity;
import net.minecraft.entity.attribute.AttributeContainer;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.impl.tool.attribute.ItemStackContext;
@Mixin(LivingEntity.class)
public class LivingEntityMixin {
@Nullable
@Unique private ItemStack stackContext = null;
@Nullable
@Unique private EquipmentSlot slotContext = null;
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/attribute/AttributeContainer;removeModifiers(Lcom/google/common/collect/Multimap;)V"), method = "method_30129", locals = LocalCapture.CAPTURE_FAILHARD)
private void storeRemoveStackContext(CallbackInfoReturnable<Map> cir, Map map, EquipmentSlot[] var2, int var3, int var4, EquipmentSlot equipmentSlot, ItemStack oldStack, ItemStack newStack) {
stackContext = oldStack;
slotContext = equipmentSlot;
}
@Redirect(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/attribute/AttributeContainer;removeModifiers(Lcom/google/common/collect/Multimap;)V"), method = "method_30129")
private void setupRemoveModifierContext(AttributeContainer attributeContainer, Multimap<EntityAttribute, EntityAttributeModifier> oldModifiers) {
((ItemStackContext) (Object) stackContext).fabricToolAttributes_setContext((LivingEntity) (Object) this);
Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = stackContext.getAttributeModifiers(slotContext);
((ItemStackContext) (Object) stackContext).fabricToolAttributes_setContext(null);
attributeContainer.removeModifiers(attributeModifiers);
}
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/attribute/AttributeContainer;addTemporaryModifiers(Lcom/google/common/collect/Multimap;)V"), method = "method_30129", locals = LocalCapture.CAPTURE_FAILHARD)
private void storeAddStackContext(CallbackInfoReturnable<Map> cir, Map map, EquipmentSlot[] var2, int var3, int var4, EquipmentSlot equipmentSlot, ItemStack oldStack, ItemStack newStack) {
stackContext = newStack;
slotContext = equipmentSlot;
}
@Redirect(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/attribute/AttributeContainer;addTemporaryModifiers(Lcom/google/common/collect/Multimap;)V"), method = "method_30129")
private void setupAddModifierContext(AttributeContainer attributeContainer, Multimap<EntityAttribute, EntityAttributeModifier> oldModifiers) {
((ItemStackContext) (Object) stackContext).fabricToolAttributes_setContext((LivingEntity) (Object) this);
Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = stackContext.getAttributeModifiers(slotContext);
((ItemStackContext) (Object) stackContext).fabricToolAttributes_setContext(null);
attributeContainer.addTemporaryModifiers(attributeModifiers);
}
}

View file

@ -16,29 +16,39 @@
package net.fabricmc.fabric.mixin.tool.attribute;
import java.util.List;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.BlockState;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.item.Item;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.api.tool.attribute.v1.ToolManager;
import net.fabricmc.fabric.impl.tool.attribute.AttributeManager;
import net.fabricmc.fabric.impl.tool.attribute.ItemStackContext;
@Mixin(ItemStack.class)
public abstract class MixinItemStack {
@Shadow
public abstract Item getItem();
public abstract class MixinItemStack implements ItemStackContext {
@Unique
@Nullable
private LivingEntity contextEntity = null;
@Inject(at = @At("RETURN"), method = "isSuitableFor", cancellable = true)
public void isEffectiveOn(BlockState state, CallbackInfoReturnable<Boolean> info) {
@ -54,14 +64,44 @@ public abstract class MixinItemStack {
}
}
// This inject stores context about the player viewing an ItemStack's tooltip before attributes are calculated.
@Environment(EnvType.CLIENT)
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getAttributeModifiers(Lnet/minecraft/entity/EquipmentSlot;)Lcom/google/common/collect/Multimap;"), method = "getTooltip")
private void storeTooltipAttributeEntityContext(PlayerEntity player, TooltipContext context, CallbackInfoReturnable<List<Text>> cir) {
contextEntity = player;
}
// This inject removes context specified in the previous inject.
// This is done to prevent issues with other mods calling getAttributeModifiers.
@Environment(EnvType.CLIENT)
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getAttributeModifiers(Lnet/minecraft/entity/EquipmentSlot;)Lcom/google/common/collect/Multimap;", shift = At.Shift.AFTER), method = "getTooltip")
private void revokeTooltipAttributeEntityContext(PlayerEntity player, TooltipContext context, CallbackInfoReturnable<List<Text>> cir) {
contextEntity = null;
}
@Inject(at = @At("RETURN"), method = "getAttributeModifiers", cancellable = true, locals = LocalCapture.CAPTURE_FAILEXCEPTION)
public void getAttributeModifiers(EquipmentSlot slot, CallbackInfoReturnable<Multimap<EntityAttribute, EntityAttributeModifier>> info, Multimap<EntityAttribute, EntityAttributeModifier> multimap) {
ItemStack stack = (ItemStack) (Object) this;
// Only perform our custom operations if the tool being operated on is dynamic.
if (stack.getItem() instanceof DynamicAttributeTool) {
// The Multimap passed in is not ordered, so we need to re-assemble the vanilla and modded attributes
// into a custom, ordered Multimap. If this step is not done, and both vanilla + modded attributes
// exist at once, the item tooltip attribute lines will randomly switch positions.
LinkedListMultimap<EntityAttribute, EntityAttributeModifier> orderedAttributes = LinkedListMultimap.create();
// First, add all vanilla attributes to our ordered Multimap.
orderedAttributes.putAll(multimap);
// Second, calculate the dynamic attributes, and add them at the end of our Multimap.
DynamicAttributeTool holder = (DynamicAttributeTool) stack.getItem();
Multimap<EntityAttribute, EntityAttributeModifier> ret = AttributeManager.mergeAttributes(multimap, (holder).getDynamicModifiers(slot, stack, null));
info.setReturnValue(ret);
orderedAttributes.putAll(holder.getDynamicModifiers(slot, stack, contextEntity));
info.setReturnValue(orderedAttributes);
}
}
@Override
public void fabricToolAttributes_setContext(@Nullable LivingEntity contextEntity) {
this.contextEntity = contextEntity;
}
}

View file

@ -1,61 +0,0 @@
/*
* 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.tool.attribute;
import com.google.common.collect.Multimap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.impl.tool.attribute.AttributeManager;
@Mixin(LivingEntity.class)
public abstract class MixinLivingEntity extends Entity {
public MixinLivingEntity(EntityType<?> type, World world) {
super(type, world);
}
/**
* @author B0undarybreaker
* @reason get entity attribute modifiers for dynamic tools
*/
@Redirect(method = "method_30129", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getAttributeModifiers(Lnet/minecraft/entity/EquipmentSlot;)Lcom/google/common/collect/Multimap;"))
public Multimap<EntityAttribute, EntityAttributeModifier> actTickModifiers(ItemStack stack, EquipmentSlot slot) {
return actModifiers(stack, slot, (LivingEntity) (Object) this);
}
private static Multimap<EntityAttribute, EntityAttributeModifier> actModifiers(ItemStack stack, EquipmentSlot slot, LivingEntity user) {
Multimap<EntityAttribute, EntityAttributeModifier> original = stack.getAttributeModifiers(slot);
if (stack.getItem() instanceof DynamicAttributeTool) {
DynamicAttributeTool tool = (DynamicAttributeTool) stack.getItem();
return (AttributeManager.mergeAttributes(original, tool.getDynamicModifiers(slot, stack, user)));
}
return original;
}
}

View file

@ -4,8 +4,8 @@
"compatibilityLevel": "JAVA_8",
"mixins": [
"BambooBlockMixin",
"MixinItemStack",
"MixinLivingEntity"
"LivingEntityMixin",
"MixinItemStack"
],
"injectors": {
"defaultRequire": 1

View file

@ -41,6 +41,10 @@ import net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder;
import net.fabricmc.fabric.api.tag.TagRegistry;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags;
import net.fabricmc.fabric.test.tool.attribute.item.TestDynamicCancelItem;
import net.fabricmc.fabric.test.tool.attribute.item.TestDynamicSwordItem;
import net.fabricmc.fabric.test.tool.attribute.item.TestDynamicToolItem;
import net.fabricmc.fabric.test.tool.attribute.item.TestNullableItem;
public class ToolAttributeTest implements ModInitializer {
private static final float DEFAULT_BREAK_SPEED = 1.0F;
@ -101,6 +105,16 @@ public class ToolAttributeTest implements ModInitializer {
.sounds(BlockSoundGroup.CROP)));
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "tater_effective_block"), new BlockItem(taterEffectiveBlock, new Item.Settings()));
// DYNAMIC ATTRIBUTE MODIFIERS
// The Dynamic Sword tests to make sure standard vanilla attributes can co-exist with dynamic attributes.
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "dynamic_sword"), new TestDynamicSwordItem(new Item.Settings()));
// The Dynamic Tool ensures a tool can have dynamic attributes (with no vanilla atributes). It applies 2 layers of speed reduction to the player.
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "dynamic_tool"), new TestDynamicToolItem(new Item.Settings()));
// Test cancels-out attributes
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "cancel_item"), new TestDynamicCancelItem(new Item.Settings()));
// Test parameter nullability
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "null_test"), new TestNullableItem(new Item.Settings()));
ServerTickEvents.START_SERVER_TICK.register(this::validate);
}

View file

@ -0,0 +1,53 @@
/*
* 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.tool.attribute.item;
import java.util.UUID;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
public class TestDynamicCancelItem extends Item implements DynamicAttributeTool {
public static final UUID TEST_UUID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF");
public TestDynamicCancelItem(Settings settings) {
super(settings);
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getDynamicModifiers(EquipmentSlot slot, ItemStack stack, @Nullable LivingEntity user) {
if (slot.equals(EquipmentSlot.MAINHAND)) {
ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> builder = ImmutableMultimap.builder();
builder.put(EntityAttributes.GENERIC_MOVEMENT_SPEED, new EntityAttributeModifier(TEST_UUID, "TEST", -1.0, EntityAttributeModifier.Operation.ADDITION));
builder.put(EntityAttributes.GENERIC_MOVEMENT_SPEED, new EntityAttributeModifier(TEST_UUID, "TEST", 1.0, EntityAttributeModifier.Operation.ADDITION));
return builder.build();
} else {
return EMPTY;
}
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.tool.attribute.item;
import java.util.UUID;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.ItemStack;
import net.minecraft.item.SwordItem;
import net.minecraft.item.ToolMaterials;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
public class TestDynamicSwordItem extends SwordItem implements DynamicAttributeTool {
public static final UUID TEST_UUID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF");
public TestDynamicSwordItem(Settings settings) {
super(ToolMaterials.DIAMOND, 6, -2.4f, settings);
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getDynamicModifiers(EquipmentSlot slot, ItemStack stack, @Nullable LivingEntity user) {
if (slot.equals(EquipmentSlot.MAINHAND)) {
ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> builder = ImmutableMultimap.builder();
builder.put(EntityAttributes.GENERIC_ATTACK_DAMAGE, new EntityAttributeModifier(TEST_UUID, "TEST", 2.0, EntityAttributeModifier.Operation.MULTIPLY_TOTAL));
builder.put(EntityAttributes.GENERIC_ATTACK_DAMAGE, new EntityAttributeModifier(TEST_UUID, "TEST2", 2.0, EntityAttributeModifier.Operation.MULTIPLY_TOTAL));
return builder.build();
} else {
return EMPTY;
}
}
}

View file

@ -0,0 +1,53 @@
/*
* 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.tool.attribute.item;
import java.util.UUID;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
public class TestDynamicToolItem extends Item implements DynamicAttributeTool {
public static final UUID TEST_UUID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF");
public TestDynamicToolItem(Settings settings) {
super(settings);
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getDynamicModifiers(EquipmentSlot slot, ItemStack stack, @Nullable LivingEntity user) {
if (slot.equals(EquipmentSlot.MAINHAND)) {
ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> builder = ImmutableMultimap.builder();
builder.put(EntityAttributes.GENERIC_MOVEMENT_SPEED, new EntityAttributeModifier(TEST_UUID, "TEST", -1.0, EntityAttributeModifier.Operation.ADDITION));
builder.put(EntityAttributes.GENERIC_MOVEMENT_SPEED, new EntityAttributeModifier(TEST_UUID, "TEST", -1.0, EntityAttributeModifier.Operation.ADDITION));
return builder.build();
} else {
return EMPTY;
}
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.tool.attribute.item;
import java.util.UUID;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
public class TestNullableItem extends Item implements DynamicAttributeTool {
public static final UUID TEST_UUID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF");
public TestNullableItem(Settings settings) {
super(settings);
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getDynamicModifiers(EquipmentSlot slot, ItemStack stack, @Nullable LivingEntity user) {
if (slot.equals(EquipmentSlot.MAINHAND)) {
ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> builder = ImmutableMultimap.builder();
builder.put(EntityAttributes.GENERIC_MOVEMENT_SPEED, new EntityAttributeModifier(TEST_UUID, "Increasing speed", user == null ? 0 : user.age * 0.001, EntityAttributeModifier.Operation.ADDITION));
return builder.build();
} else {
return EMPTY;
}
}
}