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

De-hardcode shears (again) #3477

Open
wants to merge 29 commits into
base: 1.20.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2a147b9
SHEARS!
AnAwesomGuy Dec 21, 2023
f9eae7b
shear away the accesswidener
AnAwesomGuy Dec 21, 2023
a01db88
code cleanup
AnAwesomGuy Dec 22, 2023
63da6b4
move static initializer in MatchToolLootConditionMixin to a ModInitia…
AnAwesomGuy Dec 24, 2023
c3ee57f
small change
AnAwesomGuy Dec 26, 2023
955563e
write comments and stuff like that (code cleanup x2)
AnAwesomGuy Dec 28, 2023
b3e7cf3
fix a small problem with the dispenser mixin
AnAwesomGuy Dec 28, 2023
f28040a
stack-awareness (pt. 1 (loot doesnt work yet))
AnAwesomGuy Dec 29, 2023
11f8fa1
custom item predicate
deirn Dec 30, 2023
27200b2
replace $ in fmj with \u0024
deirn Dec 31, 2023
a6dd97c
use static registry method
deirn Jan 1, 2024
6f2536e
Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/…
deirn Jan 3, 2024
9d922b4
!
deirn Jan 3, 2024
5dfeed7
gametest
AnAwesomGuy Jan 3, 2024
8a847fa
move the tag (also suppress some warnings)
AnAwesomGuy Jan 3, 2024
184aa22
actually test matching stacks :)
deirn Jan 3, 2024
4238571
tabs
deirn Jan 3, 2024
324f2c1
gametest but it still doesnt work
AnAwesomGuy Jan 5, 2024
9f390a7
use Codec#parse
deirn Jan 6, 2024
a26b354
use static block
deirn Jan 8, 2024
5ba68c1
gametest except it actually works fr this time
AnAwesomGuy Jan 11, 2024
e04af50
merge custom item predicates into my branch
AnAwesomGuy Jan 21, 2024
7225caf
Merge remote-tracking branch 'upstream/1.20.4' into 1.20.4
AnAwesomGuy Jan 21, 2024
cc2639a
whoops
AnAwesomGuy Jan 24, 2024
4929c92
stack-awareness (pt. 2) (loot works now)
AnAwesomGuy Jan 24, 2024
9eafc64
delete the accessor, its not needed now
AnAwesomGuy Jan 24, 2024
55f0e56
whoops x2
AnAwesomGuy Jan 25, 2024
c2b9129
finished adding custom predicate
AnAwesomGuy Jan 27, 2024
975798f
MY VPN WORKED!!!!
AnAwesomGuy Jan 30, 2024
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
4 changes: 2 additions & 2 deletions checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@

<!-- require blank after } in the same indentation level -->
<module name="RegexpMultiline">
<!-- \n<indentation>}\n<same indentation><whatever unless newline, '}' or starting with cas(e) or def(ault)> -->
<property name="format" value="(?&lt;=\n)([\t]+)\}\r?\n\1(?:[^\r\n\}cd]|c[^\r\na]|ca[^\r\ns]|d[^\r\ne]|de[^\r\nf])"/>
<!-- \n<indentation>}\n<same indentation><whatever unless newline, '}' or starting with cas(e), def(ault), or """ (multiline closing)> -->
<property name="format" value="(?&lt;=\n)([\t]+)\}\r?\n\1(?!(?![^\r\n\}cd]|c[^\r\na]|ca[^\r\ns]|d[^\r\ne]|de[^\r\nf])|(\&quot;\&quot;\&quot;))"/>
<property name="message" value="missing blank line after block at same indentation level"/>
</module>

Expand Down
1 change: 1 addition & 0 deletions fabric-item-api-v1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/src/testmod/generated
22 changes: 21 additions & 1 deletion fabric-item-api-v1/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
version = getSubprojectVersion(project)

moduleDependencies(project, ['fabric-api-base'])
moduleDependencies(project, [
'fabric-api-base',
'fabric-convention-tags-v1',
'fabric-lifecycle-events-v1'
])

testDependencies(project, [
':fabric-content-registries-v0',
':fabric-data-generation-api-v1'
])

loom {
runs {
datagen {
inherit testmodServer
name "Data Generation"
vmArg "-Dfabric-api.datagen"
vmArg "-Dfabric-api.datagen.output-dir=${file("src/testmod/generated")}"
vmArg "-Dfabric-api.datagen.strict-validation"

ideConfigGenerated = true
runDir "build/datagen"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.Predicate;

import com.mojang.serialization.Codec;

import net.minecraft.item.ItemStack;
import net.minecraft.predicate.item.ItemPredicate;
import net.minecraft.util.Identifier;

/**
* Allows for adding custom {@linkplain ItemPredicate item predicate}s, used in advancements and loot tables.
*
* <p>Custom predicates can be added with its registered id as the key.
* <b>Example</b>, in an advancement criterion: <pre>{@code
* "trigger": "minecraft:using_item",
* "conditions": {
* "item": {
* // vanilla values
* "durability": { "max": 20 },
* "count": { "min": 3 },
*
* // custom values
* "mymod:int": 3,
* "mymod:object": {
* "something": true
* }
* }
* }
* }</pre>
*/
public interface CustomItemPredicate extends Predicate<ItemStack> {
/**
* Returns the codec for this predicate.
*
* <p>The codec needs to also be {@linkplain CustomItemPredicateRegistry#register(Identifier, Codec) registered}.
*/
Codec<? extends CustomItemPredicate> getCodec();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 com.google.common.base.Preconditions;
import com.mojang.serialization.Codec;

import net.minecraft.util.Identifier;

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

public final class CustomItemPredicateRegistry {
/**
* Registers a codec to be used to serialize/deserialize a custom item predicate.
*
* @param id the predicate id
* @param codec the predicate codec
*/
@SuppressWarnings("unchecked")
public static void register(Identifier id, Codec<? extends CustomItemPredicate> codec) {
Preconditions.checkArgument(!FabricItemPredicateCodec.REGISTRY.containsKey(id), "There's already a codec that registered with this id");

if (FabricItemPredicateCodec.REGISTRY.containsValue(codec)) {
throw new IllegalArgumentException("The codec is already registered with id " + FabricItemPredicateCodec.REGISTRY.inverse().get(codec));
}

FabricItemPredicateCodec.REGISTRY.put(id, (Codec<CustomItemPredicate>) codec);
}

private CustomItemPredicateRegistry() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@
import org.jetbrains.annotations.Nullable;

import net.minecraft.block.BlockState;
import net.minecraft.block.dispenser.ShearsDispenserBehavior;
import net.minecraft.enchantment.EfficiencyEnchantment;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.Shearable;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ShearsItem;
import net.minecraft.loot.condition.MatchToolLootCondition;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Hand;

import net.fabricmc.fabric.impl.tag.convention.TagRegistration;

/**
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
*
Expand All @@ -38,6 +47,8 @@
* to be evaluated on a case-by-case basis. Otherwise, they are better suited for more specialized APIs.
*/
public interface FabricItem {
TagKey<Item> FABRIC_SHEARS = TagRegistration.ITEM_TAG_REGISTRATION.registerFabric("shears");

/**
* When the NBT of an item stack in the main hand or off hand changes, vanilla runs an "update animation".
* This function is called on the client side when the NBT or count of the stack has changed, but not the item,
Expand All @@ -49,6 +60,7 @@ public interface FabricItem {
* @param newStack the new stack, also of this item
* @return true to run the vanilla animation, false to cancel it.
*/
@SuppressWarnings("JavadocReference")
default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack oldStack, ItemStack newStack) {
return true;
}
Expand Down Expand Up @@ -93,6 +105,21 @@ default boolean isSuitableFor(ItemStack stack, BlockState state) {
return ((Item) this).isSuitableFor(state);
}

/**
* Determines if this item should behave like shears.
* To act like shears means to be able to {@linkplain Shearable#sheared(SoundCategory) shear mobs}, pumpkins, and beehives, {@linkplain MatchToolLootCondition harvest grass, cobwebs, and etc.}, be {@linkplain EfficiencyEnchantment#isAcceptableItem(ItemStack) enchanted with efficiency}, disarm tripwire, and have {@link ShearsDispenserBehavior} if there isn't one already registered.
*
* <p>The default implementation checks if {@code this} is an instance of {@link ShearsItem} or in the {@link #FABRIC_SHEARS #fabric:shears} tag.
*
* <p>If you want to check if a stack should be shears, it is recommended to use the stack version of this method: {@link FabricItemStack#isShears()}.
*
* @param stack the current stack
* @return {@code true} if this item should behave like shears.
*/
default boolean isShears(ItemStack stack) {
return this instanceof ShearsItem || stack.isIn(FABRIC_SHEARS);
}

/**
* Returns a leftover item stack after {@code stack} is consumed in a recipe.
* (This is also known as "recipe remainder".)
Expand Down Expand Up @@ -121,6 +148,7 @@ default boolean isSuitableFor(ItemStack stack, BlockState state) {
* @param stack the consumed {@link ItemStack}
* @return the leftover item stack
*/
@SuppressWarnings("DataFlowIssue")
default ItemStack getRecipeRemainder(ItemStack stack) {
return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.List;

import org.jetbrains.annotations.ApiStatus;

import net.minecraft.predicate.item.ItemPredicate;

@ApiStatus.NonExtendable
public interface FabricItemPredicate {
default List<CustomItemPredicate> custom() {
throw new AssertionError("Should be interface injected");
}

@ApiStatus.NonExtendable
interface FabricBuilder {
default ItemPredicate.Builder custom(CustomItemPredicate... predicate) {
throw new AssertionError("Should be interface injected");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,15 @@ default ItemStack getRecipeRemainder() {
default @Nullable FoodComponent getFoodComponent() {
return ((ItemStack) this).getItem().getFoodComponent(((ItemStack) this));
}

/**
* Determines if this item should act like shears.
*
* <p>See {@link FabricItem#isShears(ItemStack)} for a more in-depth description.
*
* @return {@code true} if this stack should behave like shears
*/
default boolean isShears() {
return ((ItemStack) this).getItem().isShears((ItemStack) this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.MapLike;
import com.mojang.serialization.RecordBuilder;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jetbrains.annotations.Nullable;

import net.minecraft.predicate.item.ItemPredicate;
import net.minecraft.util.Identifier;
import net.minecraft.util.dynamic.RuntimeOps;

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

public final class FabricItemPredicateCodec extends MapCodec<ItemPredicate> {
public static final BiMap<Identifier, Codec<CustomItemPredicate>> REGISTRY = HashBiMap.create();

public static Codec<ItemPredicate> vanillaCodec;
public static Codec<ItemPredicate> customCodec;

private final MapCodec<ItemPredicate> vanilla;
private final Set<String> vanillaKeys;

public static Codec<ItemPredicate> init(Codec<ItemPredicate> vanilla) {
vanillaCodec = vanilla;
customCodec = new FabricItemPredicateCodec(vanilla).codec();
return customCodec;
}

private FabricItemPredicateCodec(Codec<ItemPredicate> vanilla) {
this.vanilla = ((MapCodecCodec<ItemPredicate>) vanilla).codec();
this.vanillaKeys = this.vanilla.keys(RuntimeOps.INSTANCE).map(Object::toString).collect(Collectors.toUnmodifiableSet());
}

@Override
public <T> Stream<T> keys(DynamicOps<T> ops) {
Stream<T> custom = REGISTRY.keySet().stream().map(id -> ops.createString(id.toString()));
return Stream.concat(vanilla.keys(ops), custom);
}

@Override
public <T> DataResult<ItemPredicate> decode(DynamicOps<T> ops, MapLike<T> input) {
return vanilla.decode(ops, input).flatMap(predicate -> {
MutableObject<DataResult<ItemPredicate>> result = new MutableObject<>(DataResult.success(predicate));
ImmutableList.Builder<CustomItemPredicate> customs = new ImmutableList.Builder<>();

input.entries().forEach(entry -> {
if (result.getValue().error().isPresent()) return;

result.setValue(Identifier.CODEC.parse(ops, entry.getFirst()).flatMap(customId -> {
if (customId.getNamespace().equals(Identifier.DEFAULT_NAMESPACE) && vanillaKeys.contains(customId.getPath())) {
return result.getValue();
}

@Nullable Codec<CustomItemPredicate> customCodec = REGISTRY.get(customId);
if (customCodec == null) return DataResult.error(() -> "Unknown custom predicate id " + customId);

return customCodec.parse(ops, entry.getSecond()).map(custom -> {
customs.add(custom);
return predicate;
});
}));
});

return result.getValue().map(p -> {
((ItemPredicateExtensions) (Object) p).fabric_setCustom(customs.build());
return p;
});
});
}

@Override
public <T> RecordBuilder<T> encode(ItemPredicate input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
RecordBuilder<T> builder = vanilla.encode(input, ops, prefix);

for (CustomItemPredicate custom : input.custom()) {
@SuppressWarnings("unchecked")
Codec<CustomItemPredicate> customCodec = (Codec<CustomItemPredicate>) custom.getCodec();

String customId = REGISTRY.inverse().get(customCodec).toString();
builder.add(customId, customCodec.encodeStart(ops, custom));
}

return builder;
}
}
Loading