Temp fix for feature renderer testmod, update mappings, reimplement client entity events ()

* Temp fix for feature renderer testmod, update mappings

* Deprecation comments...

* Reimplement client entity events

* Readd the client world change entity event stuff
This commit is contained in:
i509VCB 2020-11-07 13:54:09 -06:00 committed by GitHub
parent c097c2c675
commit 8d074a03a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 134 additions and 63 deletions
build.gradle
fabric-lifecycle-events-v1/src/main
fabric-object-builder-api-v1/src/main
fabric-object-builders-v0/src/main/java/net/fabricmc/fabric/api/block
fabric-renderer-registries-v1/src
main/java/net/fabricmc/fabric/mixin/client/renderer/registry
testmod/java/net/fabricmc/fabric/test/renderer/registry
fabric-tool-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/tool/attribute

View file

@ -20,7 +20,7 @@ def ENV = System.getenv()
class Globals {
static def baseVersion = "0.25.3"
static def mcVersion = "20w45a"
static def yarnVersion = "+build.6"
static def yarnVersion = "+build.10"
static def loaderVersion = "0.10.5+build.213"
}

View file

@ -28,7 +28,7 @@ import net.minecraft.server.world.ServerWorld;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
@Mixin(targets = "net/minecraft/server/world/ServerWorld$class_5526")
@Mixin(targets = "net/minecraft/server/world/ServerWorld$EntityLoader")
abstract class ServerWorldEntityLoaderMixin {
// final synthetic Lnet/minecraft/server/world/ServerWorld; field_26936
@SuppressWarnings("ShadowTarget")
@ -37,7 +37,7 @@ abstract class ServerWorldEntityLoaderMixin {
private ServerWorld field_26936;
// onLoadEntity
@Inject(method = "method_31798(Lnet/minecraft/entity/Entity;)V", at = @At("TAIL"))
@Inject(method = "onLoadEntity(Lnet/minecraft/entity/Entity;)V", at = @At("TAIL"))
private void invokeEntityLoadEvent(Entity entity, CallbackInfo ci) {
ServerEntityEvents.ENTITY_LOAD.invoker().onLoad(entity, this.field_26936);
}

View file

@ -44,7 +44,7 @@ public abstract class ClientChunkManagerMixin {
@Shadow
private ClientWorld world;
@Inject(method = "loadChunkFromPacket", at = @At("TAIL")) // 1.16 has a boolean param here. I think it means whether the packet is complete.
@Inject(method = "loadChunkFromPacket", at = @At("TAIL"))
private void onChunkLoad(int x, int z, @Nullable BiomeArray biomes, PacketByteBuf buf, CompoundTag tag, int verticalStripBitmask, CallbackInfoReturnable<WorldChunk> info) {
ClientChunkEvents.CHUNK_LOAD.invoker().onChunkLoad(this.world, info.getReturnValue());
}

View file

@ -17,16 +17,25 @@
package net.fabricmc.fabric.mixin.event.lifecycle.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
@Environment(EnvType.CLIENT)
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
/*@Shadow
@Shadow
private ClientWorld world;
@Inject(method = "onPlayerRespawn", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
@ -37,12 +46,12 @@ public abstract class ClientPlayNetworkHandlerMixin {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
/*for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}*/
}
}*/
}
/**
* An explanation why we unload entities during onGameJoin:
@ -50,7 +59,7 @@ public abstract class ClientPlayNetworkHandlerMixin {
* Velocity by default will send a Game Join packet when the player changes servers, which will create a new client world.
* Also anyone can send another GameJoinPacket at any time, so we need to watch out.
*/
/*@Inject(method = "onGameJoin", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
@Inject(method = "onGameJoin", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
private void onGameJoin(GameJoinS2CPacket packet, CallbackInfo ci) {
// If a world already exists, we need to unload all (block)entities in the world.
if (this.world != null) {
@ -58,10 +67,10 @@ public abstract class ClientPlayNetworkHandlerMixin {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
/*for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}*/
}
}
@ -74,10 +83,10 @@ public abstract class ClientPlayNetworkHandlerMixin {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
/*for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}*/
}
}*/
}
}

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.mixin.event.lifecycle.client;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
@Environment(EnvType.CLIENT)
@Mixin(targets = "net/minecraft/client/world/ClientWorld$EntityLoader")
abstract class ClientWorldEntityLoaderMixin {
// final synthetic Lnet/minecraft/client/world/ClientWorld; field_27735
@SuppressWarnings("ShadowTarget")
@Shadow
@Final
private ClientWorld field_27735;
// Call our load event after vanilla has loaded the entity
@Inject(method = "onLoadEntity", at = @At("TAIL"))
private void invokeLoadEntity(Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_LOAD.invoker().onLoad(entity, this.field_27735);
}
// Call our unload event before vanilla does.
@Inject(method = "onUnloadEntity", at = @At("HEAD"))
private void invokeUnloadEntity(Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.field_27735);
}
}

View file

@ -31,18 +31,6 @@ import net.fabricmc.fabric.mixin.event.lifecycle.WorldMixin;
@Environment(EnvType.CLIENT)
@Mixin(ClientWorld.class)
public abstract class ClientWorldMixin extends WorldMixin {
/*// Call our load event after vanilla has loaded the entity
@Inject(method = "addEntityPrivate", at = @At("TAIL"))
private void onEntityLoad(int id, Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_LOAD.invoker().onLoad(entity, (ClientWorld) (Object) this);
}
// Call our unload event before vanilla does.
@Inject(method = "finishRemovingEntity", at = @At("HEAD"))
private void onEntityUnload(Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, (ClientWorld) (Object) this);
}*/
// We override our injection on the clientworld so only the client's block entity invocations will run
/*@Override
protected void onLoadBlockEntity(BlockEntity blockEntity, CallbackInfoReturnable<Boolean> cir) {

View file

@ -13,6 +13,7 @@
"client.ClientChunkManagerMixin",
"client.ClientPlayNetworkHandlerMixin",
"client.ClientWorldMixin",
"client.ClientWorldEntityLoaderMixin",
"client.MinecraftClientMixin"
],
"injectors": {

View file

@ -21,8 +21,8 @@ import java.util.function.ToIntFunction;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.sound.BlockSoundGroup;
@ -43,12 +43,12 @@ import net.fabricmc.fabric.mixin.object.builder.AbstractBlockSettingsAccessor;
* FabricBlockSettings.of().
*/
public class FabricBlockSettings extends AbstractBlock.Settings {
protected FabricBlockSettings(Material material, MaterialColor color) {
protected FabricBlockSettings(Material material, MapColor color) {
super(material, color);
}
protected FabricBlockSettings(AbstractBlock.Settings settings) {
super(((AbstractBlockSettingsAccessor) settings).getMaterial(), ((AbstractBlockSettingsAccessor) settings).getMaterialColorFactory());
super(((AbstractBlockSettingsAccessor) settings).getMaterial(), ((AbstractBlockSettingsAccessor) settings).getMapColorProvider());
// Mostly Copied from vanilla's copy method
// Note: If new methods are added to Block settings, an accessor must be added here
AbstractBlockSettingsAccessor thisAccessor = (AbstractBlockSettingsAccessor) this;
@ -60,7 +60,7 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
this.collidable(otherAccessor.getCollidable());
thisAccessor.setRandomTicks(otherAccessor.getRandomTicks());
this.luminance(otherAccessor.getLuminance());
thisAccessor.setMaterialColorFactory(otherAccessor.getMaterialColorFactory());
thisAccessor.setMapColorProvider(otherAccessor.getMapColorProvider());
this.sounds(otherAccessor.getSoundGroup());
this.slipperiness(otherAccessor.getSlipperiness());
this.velocityMultiplier(otherAccessor.getVelocityMultiplier());
@ -82,12 +82,12 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
return of(material, material.getColor());
}
public static FabricBlockSettings of(Material material, MaterialColor color) {
public static FabricBlockSettings of(Material material, MapColor color) {
return new FabricBlockSettings(material, color);
}
public static FabricBlockSettings of(Material material, DyeColor color) {
return new FabricBlockSettings(material, color.getMaterialColor());
return new FabricBlockSettings(material, color.getMapColor());
}
public static FabricBlockSettings copyOf(AbstractBlock block) {
@ -272,13 +272,29 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
/* FABRIC DELEGATE WRAPPERS */
public FabricBlockSettings materialColor(MaterialColor color) {
((AbstractBlockSettingsAccessor) this).setMaterialColorFactory(ignored -> color);
/**
* @deprecated Please migrate to {@link FabricBlockSettings#mapColor(MapColor)}
*/
@Deprecated
public FabricBlockSettings materialColor(MapColor color) {
return this.mapColor(color);
}
/**
* @deprecated Please migrate to {@link FabricBlockSettings#mapColor(DyeColor)}
*/
@Deprecated
public FabricBlockSettings materialColor(DyeColor color) {
return this.mapColor(color);
}
public FabricBlockSettings mapColor(MapColor color) {
((AbstractBlockSettingsAccessor) this).setMapColorProvider(ignored -> color);
return this;
}
public FabricBlockSettings materialColor(DyeColor color) {
return this.materialColor(color.getMaterialColor());
public FabricBlockSettings mapColor(DyeColor color) {
return this.mapColor(color.getMapColor());
}
public FabricBlockSettings collidable(boolean collidable) {

View file

@ -16,20 +16,20 @@
package net.fabricmc.fabric.api.object.builder.v1.block;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.util.DyeColor;
import net.fabricmc.fabric.mixin.object.builder.MaterialBuilderAccessor;
public class FabricMaterialBuilder extends Material.Builder {
public FabricMaterialBuilder(MaterialColor color) {
public FabricMaterialBuilder(MapColor color) {
super(color);
}
public FabricMaterialBuilder(DyeColor color) {
this(color.getMaterialColor());
this(color.getMapColor());
}
@Override

View file

@ -26,8 +26,8 @@ import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.Identifier;
@ -53,7 +53,7 @@ public interface AbstractBlockSettingsAccessor {
ToIntFunction<BlockState> getLuminance();
@Accessor
Function<BlockState, MaterialColor> getMaterialColorFactory();
Function<BlockState, MapColor> getMapColorProvider();
@Accessor
BlockSoundGroup getSoundGroup();
@ -93,7 +93,7 @@ public interface AbstractBlockSettingsAccessor {
void setRandomTicks(boolean ticksRandomly);
@Accessor
void setMaterialColorFactory(Function<BlockState, MaterialColor> materialColorFunction);
void setMapColorProvider(Function<BlockState, MapColor> mapColorProvider);
@Accessor
void setDynamicBounds(boolean dynamicBounds);

View file

@ -1,5 +1,5 @@
accessWidener v1 named
extendable method net/minecraft/block/AbstractBlock$Settings <init> (Lnet/minecraft/block/Material;Ljava/util/function/Function;)V
extendable method net/minecraft/block/AbstractBlock$Settings <init> (Lnet/minecraft/block/Material;Lnet/minecraft/block/MaterialColor;)V
extendable method net/minecraft/block/AbstractBlock$Settings <init> (Lnet/minecraft/block/Material;Lnet/minecraft/block/MapColor;)V
extendable class net/minecraft/block/entity/BlockEntityType$class_5559

View file

@ -17,7 +17,7 @@
package net.fabricmc.fabric.api.block;
import net.minecraft.block.AbstractBlock.Settings;
import net.minecraft.block.MaterialColor;
import net.minecraft.block.MapColor;
import net.minecraft.item.Item;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.tag.Tag;
@ -54,8 +54,8 @@ public final class BlockSettingsExtensions {
((AbstractBlockSettingsAccessor) settings).setCollidable(collidable);
}
public static void materialColor(Settings settings, MaterialColor materialColor) {
((AbstractBlockSettingsAccessor) settings).setMaterialColorFactory(ignored -> materialColor);
public static void materialColor(Settings settings, MapColor color) {
((AbstractBlockSettingsAccessor) settings).setMapColorProvider(ignored -> color);
}
public static void drops(Settings settings, Identifier dropTableId) {

View file

@ -19,8 +19,8 @@ package net.fabricmc.fabric.api.block;
import java.util.function.Function;
import net.minecraft.block.Block;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.item.Item;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.tag.Tag;
@ -34,7 +34,7 @@ import net.minecraft.util.Identifier;
public class FabricBlockSettings {
protected final net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings delegate;
protected FabricBlockSettings(Material material, MaterialColor color) {
protected FabricBlockSettings(Material material, MapColor color) {
this(Block.Settings.of(material, color));
}
@ -50,12 +50,12 @@ public class FabricBlockSettings {
return of(material, material.getColor());
}
public static FabricBlockSettings of(Material material, MaterialColor color) {
public static FabricBlockSettings of(Material material, MapColor color) {
return new FabricBlockSettings(material, color);
}
public static FabricBlockSettings of(Material material, DyeColor color) {
return new FabricBlockSettings(material, color.getMaterialColor());
return new FabricBlockSettings(material, color.getMapColor());
}
public static FabricBlockSettings copy(Block base) {
@ -96,13 +96,13 @@ public class FabricBlockSettings {
/* DELEGATE WRAPPERS */
public FabricBlockSettings materialColor(MaterialColor color) {
public FabricBlockSettings materialColor(MapColor color) {
this.delegate.materialColor(color);
return this;
}
public FabricBlockSettings materialColor(DyeColor color) {
this.delegate.materialColor(color.getMaterialColor());
this.delegate.materialColor(color.getMapColor());
return this;
}

View file

@ -16,8 +16,8 @@
package net.fabricmc.fabric.api.block;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.util.DyeColor;
@ -28,13 +28,13 @@ import net.minecraft.util.DyeColor;
public class FabricMaterialBuilder extends Material.Builder {
private net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder delegate;
public FabricMaterialBuilder(MaterialColor color) {
public FabricMaterialBuilder(MapColor color) {
super(color);
this.delegate = new net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder(color);
}
public FabricMaterialBuilder(DyeColor color) {
this(color.getMaterialColor());
this(color.getMapColor());
}
@Override

View file

@ -66,6 +66,7 @@ public abstract class MixinEntityRenderers {
return entityRenderer;
}
// private static synthetic method_32175(Lcom/google/common/collect/ImmutableMap$Builder;Lnet/minecraft/class_5617$class_5618;Ljava/lang/String;Lnet/minecraft/class_5617;)V
@SuppressWarnings({"unchecked", "rawtypes"})
@Redirect(method = "method_32175", at = @At(value = "INVOKE", target = "Lnet/minecraft/class_5617;create(Lnet/minecraft/class_5617$class_5618;)Lnet/minecraft/client/render/entity/EntityRenderer;"))
private static EntityRenderer<? extends PlayerEntity> createPlayerEntityRenderer(class_5617<AbstractClientPlayerEntity> playerEntityRendererFactory, class_5617.class_5618 context, ImmutableMap.Builder builder, class_5617.class_5618 context2, String str, class_5617<AbstractClientPlayerEntity> playerEntityRendererFactory2) {

View file

@ -34,7 +34,6 @@ import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendereregistry.v1.LivingEntityFeatureRendererRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
public final class FeatureRendererTest implements ClientModInitializer {
private static final Logger LOGGER = LogManager.getLogger(FeatureRendererTest.class);
@ -42,7 +41,7 @@ public final class FeatureRendererTest implements ClientModInitializer {
@Override
public void onInitializeClient() {
LOGGER.info("Registering test feature renderer");
LOGGER.info("Registering feature renderer tests");
LivingEntityFeatureRendererRegistrationCallback.EVENT.register((entityType, entityRenderer, registrationHelper, context) -> {
// minecraft:player SHOULD be printed twice
LOGGER.info(String.format("Received registration for %s", Registry.ENTITY_TYPE.getId(entityType)));
@ -56,13 +55,17 @@ public final class FeatureRendererTest implements ClientModInitializer {
}
});
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
// FIXME: Add AfterResourceReload event to client so this can be tested.
// This is due to a change in 20w45a which now means this is called after the client is initialized.
/*ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
LOGGER.info("Client is starting");
if (this.playerRegistrations != 2) {
throw new AssertionError(String.format("Expected 2 entity feature renderer registration events for \"minecraft:player\" but received %s registrations", this.playerRegistrations));
}
LOGGER.info("Successfully called feature renderer registration events");
});
});*/
}
private static class TestPlayerFeatureRenderer extends FeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {

View file

@ -19,8 +19,8 @@ package net.fabricmc.fabric.test.tool.attribute;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.MapColor;
import net.minecraft.block.Material;
import net.minecraft.block.MaterialColor;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
@ -69,7 +69,7 @@ public class ToolAttributeTest implements ModInitializer {
testPickaxe = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_pickaxe"), new TestTool(new Item.Settings(), FabricToolTags.PICKAXES, 2));
// Register a block that requires a shovel that is as strong or stronger than an iron one.
gravelBlock = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_gravel_block"),
new Block(FabricBlockSettings.of(new FabricMaterialBuilder(MaterialColor.SAND).build(), MaterialColor.STONE)
new Block(FabricBlockSettings.of(new FabricMaterialBuilder(MapColor.SAND).build(), MapColor.STONE)
.breakByTool(FabricToolTags.SHOVELS, 2)
.requiresTool()
.strength(0.6F)
@ -77,7 +77,7 @@ public class ToolAttributeTest implements ModInitializer {
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_gravel_block"), new BlockItem(gravelBlock, new Item.Settings()));
// Register a block that requires a pickaxe that is as strong or stronger than an iron one.
stoneBlock = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_stone_block"),
new Block(FabricBlockSettings.of(Material.STONE, MaterialColor.STONE)
new Block(FabricBlockSettings.of(Material.STONE, MapColor.STONE)
.breakByTool(FabricToolTags.PICKAXES, 2)
.requiresTool()
.strength(0.6F)
@ -94,7 +94,7 @@ public class ToolAttributeTest implements ModInitializer {
testDiamondDynamicLevelTater = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_diamond_dynamic_level_tater"), new TestTool(new Item.Settings(), TATER, 3));
taterEffectiveBlock = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "tater_effective_block"),
new Block(FabricBlockSettings.of(Material.ORGANIC_PRODUCT, MaterialColor.ORANGE)
new Block(FabricBlockSettings.of(Material.ORGANIC_PRODUCT, MapColor.ORANGE)
.breakByTool(TATER, 2) // requires iron tater
.requiresTool()
.strength(0.6F)