From 44e6689f3cafc7480491ec2e93ef338a366e7c63 Mon Sep 17 00:00:00 2001
From: modmuss <modmuss50@gmail.com>
Date: Wed, 10 Apr 2024 22:15:13 +0100
Subject: [PATCH] Deprecate FabricEntityTypeBuilder &
 FabricBlockEntityTypeBuilder in favour of the vanilla classes (#3677)

* Replace FabricEntityTypeBuilder with EntityTypeBuilder + iface injection

* Finish and test entity type builder

* Deprecate FabricBlockEntityTypeBuilder

* Review fix

* Fixes based on review

* Some fixes

* Checkstyle
---
 build.gradle                                  |   2 +-
 .../block/entity/FabricBlockEntityType.java   |  43 ++++++
 .../entity/FabricBlockEntityTypeBuilder.java  |  23 ++++
 .../builder/v1/entity/FabricEntityType.java   | 126 ++++++++++++++++++
 .../v1/entity/FabricEntityTypeBuilder.java    |  98 ++++++++++++--
 .../impl/object/builder/FabricEntityType.java |  44 ------
 .../object/builder/FabricEntityTypeImpl.java  | 106 +++++++++++++++
 .../builder/BlockEntityTypeBuilderMixin.java  |  37 +++++
 .../builder/EntityTypeBuilderMixin.java       | 114 ++++++++++++++++
 .../mixin/object/builder/EntityTypeMixin.java |  45 +++++++
 .../fabric-object-builder-v1.mixins.json      |   3 +
 .../src/main/resources/fabric.mod.json        |   6 +-
 .../object/builder/FabricEntityTypeTest.java  |  86 ++++++++++++
 13 files changed, 674 insertions(+), 59 deletions(-)
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityType.java
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityType.java
 delete mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityType.java
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityTypeImpl.java
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/BlockEntityTypeBuilderMixin.java
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeBuilderMixin.java
 create mode 100644 fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeMixin.java
 create mode 100644 fabric-object-builder-api-v1/src/test/java/net/fabricmc/fabric/test/object/builder/FabricEntityTypeTest.java

diff --git a/build.gradle b/build.gradle
index 861bbf523..ddcc981b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ plugins {
 	id "idea"
 	id "maven-publish"
 	id 'jacoco'
-	id "fabric-loom" version "1.6.3" apply false
+	id "fabric-loom" version "1.6.5" apply false
 	id "com.diffplug.spotless" version "6.20.0"
 	id "org.ajoberstar.grgit" version "3.1.0"
 	id "me.modmuss50.remotesign" version "0.4.0" apply false
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityType.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityType.java
new file mode 100644
index 000000000..7aeba7536
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityType.java
@@ -0,0 +1,43 @@
+/*
+ * 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.object.builder.v1.block.entity;
+
+import com.mojang.datafixers.types.Type;
+
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.block.entity.BlockEntityType;
+
+/**
+ * General-purpose Fabric-provided extensions for {@link BlockEntityType}.
+ */
+public interface FabricBlockEntityType {
+	/**
+	 * General-purpose Fabric-provided extensions for {@link BlockEntityType.Builder}.
+	 *
+	 * <p>Note: This interface is automatically implemented on {@link BlockEntityType.Builder} via Mixin and interface injection.
+	 */
+	interface Builder<T extends BlockEntity> {
+		/**
+		 * Builds the {@link BlockEntityType}, see {@link BlockEntityType.Builder#build(Type)}.
+		 *
+		 * @return the built {@link BlockEntityType}
+		 */
+		default BlockEntityType<T> build() {
+			throw new AssertionError("Implemented in Mixin");
+		}
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityTypeBuilder.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityTypeBuilder.java
index 1229c3d11..898afc17e 100644
--- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityTypeBuilder.java
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityTypeBuilder.java
@@ -33,7 +33,10 @@ import net.minecraft.util.math.BlockPos;
  *
  * <p>Alternatively, use the access widener for {@link BlockEntityType.BlockEntityFactory}
  * in Fabric Transitive Access Wideners (v1).
+ *
+ * @deprecated Use {@link BlockEntityType.Builder} directly.
  */
+@Deprecated
 public final class FabricBlockEntityTypeBuilder<T extends BlockEntity> {
 	private final Factory<? extends T> factory;
 	private final List<Block> blocks;
@@ -43,6 +46,10 @@ public final class FabricBlockEntityTypeBuilder<T extends BlockEntity> {
 		this.blocks = blocks;
 	}
 
+	/**
+	 * @deprecated Use {@link BlockEntityType.Builder#create(BlockEntityType.BlockEntityFactory, Block...)}.
+	 */
+	@Deprecated
 	public static <T extends BlockEntity> FabricBlockEntityTypeBuilder<T> create(Factory<? extends T> factory, Block... blocks) {
 		List<Block> blocksList = new ArrayList<>(blocks.length);
 		Collections.addAll(blocksList, blocks);
@@ -55,7 +62,9 @@ public final class FabricBlockEntityTypeBuilder<T extends BlockEntity> {
 	 *
 	 * @param block the supported block
 	 * @return this builder
+	 * @deprecated Use {@link BlockEntityType.Builder#create(BlockEntityType.BlockEntityFactory, Block...)}.
 	 */
+	@Deprecated
 	public FabricBlockEntityTypeBuilder<T> addBlock(Block block) {
 		this.blocks.add(block);
 		return this;
@@ -66,22 +75,36 @@ public final class FabricBlockEntityTypeBuilder<T extends BlockEntity> {
 	 *
 	 * @param blocks the supported blocks
 	 * @return this builder
+	 * @deprecated Use {@link BlockEntityType.Builder#create(BlockEntityType.BlockEntityFactory, Block...)}.
 	 */
+	@Deprecated
 	public FabricBlockEntityTypeBuilder<T> addBlocks(Block... blocks) {
 		Collections.addAll(this.blocks, blocks);
 		return this;
 	}
 
+	/**
+	 * @deprecated Use {@link BlockEntityType.Builder#build()}.
+	 */
+	@Deprecated
 	public BlockEntityType<T> build() {
 		return build(null);
 	}
 
+	/**
+	 * @deprecated Use {@link BlockEntityType.Builder#build(Type)}.
+	 */
+	@Deprecated
 	public BlockEntityType<T> build(Type<?> type) {
 		return BlockEntityType.Builder.<T>create(factory::create, blocks.toArray(new Block[0]))
 				.build(type);
 	}
 
+	/**
+	 * @deprecated Use {@link BlockEntityType.BlockEntityFactory}.
+	 */
 	@FunctionalInterface
+	@Deprecated
 	public interface Factory<T extends BlockEntity> {
 		T create(BlockPos blockPos, BlockState blockState);
 	}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityType.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityType.java
new file mode 100644
index 000000000..7f60377ac
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityType.java
@@ -0,0 +1,126 @@
+/*
+ * 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.object.builder.v1.entity;
+
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.SpawnGroup;
+import net.minecraft.entity.SpawnRestriction;
+import net.minecraft.entity.attribute.DefaultAttributeContainer;
+import net.minecraft.entity.mob.MobEntity;
+import net.minecraft.world.Heightmap;
+
+import net.fabricmc.fabric.impl.object.builder.FabricEntityTypeImpl;
+
+/**
+ * General-purpose Fabric-provided extensions for {@link EntityType}.
+ */
+public interface FabricEntityType {
+	/**
+	 * General-purpose Fabric-provided extensions for {@link EntityType.Builder}.
+	 *
+	 * <p>Note: This interface is automatically implemented on {@link EntityType.Builder} via Mixin and interface injection.
+	 */
+	interface Builder<T extends Entity> {
+		/**
+		 * Sets whether the entity's velocity should always be updated.
+		 *
+		 * @param alwaysUpdateVelocity whether the entity's velocity should always be updated
+		 * @return this builder
+		 */
+		default EntityType.Builder<T> alwaysUpdateVelocity(boolean alwaysUpdateVelocity) {
+			throw new AssertionError("Implemented in Mixin");
+		}
+
+		/**
+		 * Build the entity type from the builder. Same as {@link EntityType.Builder#build(String)} but without an id.
+		 *
+		 * @return the entity type instance
+		 */
+		default EntityType<T> build() {
+			throw new AssertionError("Implemented in Mixin");
+		}
+
+		/**
+		 * Creates an entity type builder for a living entity.
+		 *
+		 * <p>This entity's spawn group will automatically be set to {@link SpawnGroup#MISC}.
+		 *
+		 * @param <T> the type of entity
+		 * @param livingBuilder a function to configure living entity specific properties
+		 *
+		 * @return a new living entity type builder
+		 */
+		static <T extends LivingEntity> EntityType.Builder<T> createLiving(EntityType.EntityFactory<T> factory, SpawnGroup spawnGroup, UnaryOperator<Living<T>> livingBuilder) {
+			return FabricEntityTypeImpl.Builder.createLiving(factory, spawnGroup, livingBuilder);
+		}
+
+		/**
+		 * Creates an entity type builder for a mob entity.
+		 *
+		 * @param <T> the type of entity
+		 * @param mobBuilder a function to configure mob entity specific properties
+		 *
+		 * @return a new mob entity type builder
+		 */
+		static <T extends MobEntity> EntityType.Builder<T> createMob(EntityType.EntityFactory<T> factory, SpawnGroup spawnGroup, UnaryOperator<Mob<T>> mobBuilder) {
+			return FabricEntityTypeImpl.Builder.createMob(factory, spawnGroup, mobBuilder);
+		}
+
+		/**
+		 * A builder for additional properties of a living entity, use via {@link #createLiving(EntityType.EntityFactory, SpawnGroup, UnaryOperator)}.
+		 * @param <T> the type of living entity
+		 */
+		interface Living<T extends LivingEntity> {
+			/**
+			 * Sets the default attributes for a type of living entity.
+			 *
+			 * @param defaultAttributeBuilder a function to generate the default attribute builder from the entity type
+			 * @return this builder for chaining
+			 */
+			Living<T> defaultAttributes(Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder);
+		}
+
+		/**
+		 * A builder for additional properties of a mob entity, use via {@link #createMob(EntityType.EntityFactory, SpawnGroup, UnaryOperator)}.
+		 * @param <T> the type of mob entity
+		 */
+		interface Mob<T extends MobEntity> extends Living<T> {
+			/**
+			 * Registers a spawn restriction for this entity.
+			 *
+			 * <p>This is used by mobs to determine whether Minecraft should spawn an entity within a certain context.
+			 *
+			 * @return this builder for chaining.
+			 */
+			Mob<T> spawnRestriction(SpawnRestriction.Location location, Heightmap.Type heightmap, SpawnRestriction.SpawnPredicate<T> spawnPredicate);
+
+			/**
+			 * Sets the default attributes for a type of mob entity.
+			 *
+			 * @param defaultAttributeBuilder a function to generate the default attribute builder from the entity type
+			 * @return this builder for chaining
+			 */
+			@Override
+			Mob<T> defaultAttributes(Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder);
+		}
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityTypeBuilder.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityTypeBuilder.java
index d8d1b02f4..da5219497 100644
--- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityTypeBuilder.java
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityTypeBuilder.java
@@ -18,6 +18,7 @@ package net.fabricmc.fabric.api.object.builder.v1.entity;
 
 import java.util.Objects;
 import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
 
 import com.google.common.collect.ImmutableSet;
 import org.jetbrains.annotations.Nullable;
@@ -33,19 +34,13 @@ import net.minecraft.entity.SpawnRestriction;
 import net.minecraft.entity.attribute.DefaultAttributeContainer;
 import net.minecraft.entity.mob.MobEntity;
 import net.minecraft.resource.featuretoggle.FeatureFlag;
-import net.minecraft.resource.featuretoggle.FeatureFlags;
-import net.minecraft.resource.featuretoggle.FeatureSet;
 import net.minecraft.world.Heightmap;
 import net.minecraft.world.World;
 
-import net.fabricmc.fabric.impl.object.builder.FabricEntityType;
-
 /**
- * Extended version of {@link EntityType.Builder} with added registration for
- * server-&gt;client entity tracking values.
- *
- * @param <T> Entity class.
+ * @deprecated replace with {@link EntityType.Builder}
  */
+@Deprecated
 public class FabricEntityTypeBuilder<T extends Entity> {
 	private SpawnGroup spawnGroup;
 	private EntityType.EntityFactory<T> factory;
@@ -59,7 +54,8 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	private EntityDimensions dimensions = EntityDimensions.changing(-1.0f, -1.0f);
 	private ImmutableSet<Block> specificSpawnBlocks = ImmutableSet.of();
 
-	private FeatureSet requiredFeatures = FeatureFlags.VANILLA_FEATURES;
+	@Nullable
+	private FeatureFlag[] requiredFeatures = null;
 
 	protected FabricEntityTypeBuilder(SpawnGroup spawnGroup, EntityType.EntityFactory<T> factory) {
 		this.spawnGroup = spawnGroup;
@@ -75,7 +71,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param <T> the type of entity
 	 *
 	 * @return a new entity type builder
+	 * @deprecated use {@link EntityType.Builder#create(SpawnGroup)}
 	 */
+	@Deprecated
 	public static <T extends Entity> FabricEntityTypeBuilder<T> create() {
 		return create(SpawnGroup.MISC);
 	}
@@ -87,7 +85,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param <T> the type of entity
 	 *
 	 * @return a new entity type builder
+	 * @deprecated use {@link EntityType.Builder#create(SpawnGroup)}
 	 */
+	@Deprecated
 	public static <T extends Entity> FabricEntityTypeBuilder<T> create(SpawnGroup spawnGroup) {
 		return create(spawnGroup, FabricEntityTypeBuilder::emptyFactory);
 	}
@@ -100,7 +100,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param <T> the type of entity
 	 *
 	 * @return a new entity type builder
+	 * @deprecated use {@link EntityType.Builder#create(EntityType.EntityFactory, SpawnGroup)}
 	 */
+	@Deprecated
 	public static <T extends Entity> FabricEntityTypeBuilder<T> create(SpawnGroup spawnGroup, EntityType.EntityFactory<T> factory) {
 		return new FabricEntityTypeBuilder<>(spawnGroup, factory);
 	}
@@ -113,7 +115,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param <T> the type of entity
 	 *
 	 * @return a new living entity type builder
+	 * @deprecated use {@link FabricEntityType.Builder#createLiving(UnaryOperator)}
 	 */
+	@Deprecated
 	public static <T extends LivingEntity> FabricEntityTypeBuilder.Living<T> createLiving() {
 		return new FabricEntityTypeBuilder.Living<>(SpawnGroup.MISC, FabricEntityTypeBuilder::emptyFactory);
 	}
@@ -124,6 +128,7 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param <T> the type of entity
 	 *
 	 * @return a new mob entity type builder
+	 * @deprecated use {@link FabricEntityType.Builder#createMob(UnaryOperator)}
 	 */
 	public static <T extends MobEntity> FabricEntityTypeBuilder.Mob<T> createMob() {
 		return new FabricEntityTypeBuilder.Mob<>(SpawnGroup.MISC, FabricEntityTypeBuilder::emptyFactory);
@@ -133,12 +138,14 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 		return null;
 	}
 
+	@Deprecated
 	public FabricEntityTypeBuilder<T> spawnGroup(SpawnGroup group) {
 		Objects.requireNonNull(group, "Spawn group cannot be null");
 		this.spawnGroup = group;
 		return this;
 	}
 
+	@Deprecated
 	public <N extends T> FabricEntityTypeBuilder<N> entityFactory(EntityType.EntityFactory<N> factory) {
 		Objects.requireNonNull(factory, "Entity Factory cannot be null");
 		this.factory = (EntityType.EntityFactory<T>) factory;
@@ -149,12 +156,18 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * Whether this entity type is summonable using the {@code /summon} command.
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#disableSummon()}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> disableSummon() {
 		this.summonable = false;
 		return this;
 	}
 
+	/**
+	 * @deprecated use {@link EntityType.Builder#disableSaving()}
+	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> disableSaving() {
 		this.saveable = false;
 		return this;
@@ -164,7 +177,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * Sets this entity type to be fire immune.
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#makeFireImmune()}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> fireImmune() {
 		this.fireImmune = true;
 		return this;
@@ -174,7 +189,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * Sets whether this entity type can be spawned far away from a player.
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#spawnableFarFromPlayer()}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> spawnableFarFromPlayer() {
 		this.spawnableFarFromPlayer = true;
 		return this;
@@ -186,7 +203,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param dimensions the dimensions representing the entity's size
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#setDimensions(float, float)}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> dimensions(EntityDimensions dimensions) {
 		Objects.requireNonNull(dimensions, "Cannot set null dimensions");
 		this.dimensions = dimensions;
@@ -218,7 +237,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param range the tracking range in chunks
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link FabricEntityTypeBuilder#trackRangeBlocks(int)}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> trackRangeChunks(int range) {
 		this.trackRange = range;
 		return this;
@@ -230,16 +251,26 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * @param range the tracking range in blocks
 	 *
 	 * @return this builder for chaining
+	 * @deprecated use {@link FabricEntityTypeBuilder#trackRangeChunks(int)}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> trackRangeBlocks(int range) {
 		return trackRangeChunks((range + 15) / 16);
 	}
 
+	/**
+	 * @deprecated use {@link FabricEntityTypeBuilder#trackRangeBlocks(int)}
+	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> trackedUpdateRate(int rate) {
 		this.trackedUpdateRate = rate;
 		return this;
 	}
 
+	/**
+	 * @deprecated use {@link FabricEntityTypeBuilder#trackRangeBlocks(int)}
+	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> forceTrackedVelocityUpdates(boolean forceTrackedVelocityUpdates) {
 		this.forceTrackedVelocityUpdates = forceTrackedVelocityUpdates;
 		return this;
@@ -250,7 +281,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 *
 	 * @param blocks the blocks the entity can spawn on
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#allowSpawningInside(Block...)}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> specificSpawnBlocks(Block... blocks) {
 		this.specificSpawnBlocks = ImmutableSet.copyOf(blocks);
 		return this;
@@ -261,9 +294,11 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * the entity cannot be spawned, and existing ones will despawn immediately.
 	 * @param requiredFeatures the features
 	 * @return this builder for chaining
+	 * @deprecated use {@link EntityType.Builder#requires(FeatureFlag...)}
 	 */
+	@Deprecated
 	public FabricEntityTypeBuilder<T> requires(FeatureFlag... requiredFeatures) {
-		this.requiredFeatures = FeatureFlags.FEATURE_MANAGER.featureSetOf(requiredFeatures);
+		this.requiredFeatures = requiredFeatures;
 		return this;
 	}
 
@@ -271,19 +306,50 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 * Creates the entity type.
 	 *
 	 * @return a new {@link EntityType}
+	 * @deprecated use {@link EntityType.Builder#build()}
 	 */
+	@Deprecated
 	public EntityType<T> build() {
-		// Modded DFU is a dream, currently not possible without screwing it up.
+		EntityType.Builder<T> builder = EntityType.Builder.create(this.factory, this.spawnGroup)
+				.allowSpawningInside(specificSpawnBlocks.toArray(Block[]::new))
+				.maxTrackingRange(this.trackRange)
+				.trackingTickInterval(this.trackedUpdateRate)
+				.setDimensions(this.dimensions.width, this.dimensions.height);
 
-		//TODO 1.20.5, new field
-		return new FabricEntityType<>(this.factory, this.spawnGroup, this.saveable, this.summonable, this.fireImmune, this.spawnableFarFromPlayer, this.specificSpawnBlocks, dimensions, 1, trackRange, trackedUpdateRate, forceTrackedVelocityUpdates, this.requiredFeatures);
+		if (!this.saveable) {
+			builder = builder.disableSaving();
+		}
+
+		if (!this.summonable) {
+			builder = builder.disableSummon();
+		}
+
+		if (this.fireImmune) {
+			builder = builder.makeFireImmune();
+		}
+
+		if (this.spawnableFarFromPlayer) {
+			builder = builder.spawnableFarFromPlayer();
+		}
+
+		if (this.requiredFeatures != null) {
+			builder = builder.requires(this.requiredFeatures);
+		}
+
+		if (this.forceTrackedVelocityUpdates != null) {
+			builder = builder.alwaysUpdateVelocity(this.forceTrackedVelocityUpdates);
+		}
+
+		return builder.build(null);
 	}
 
 	/**
 	 * An extended version of {@link FabricEntityTypeBuilder} with support for features on present on {@link LivingEntity living entities}, such as default attributes.
 	 *
 	 * @param <T> Entity class.
+	 * @deprecated use {@link EntityType.Builder#createLiving(UnaryOperator)}
 	 */
+	@Deprecated
 	public static class Living<T extends LivingEntity> extends FabricEntityTypeBuilder<T> {
 		@Nullable
 		private Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder;
@@ -399,13 +465,16 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 		 *
 		 * @param defaultAttributeBuilder a function to generate the default attribute builder from the entity type
 		 * @return this builder for chaining
+		 * @deprecated use {@link FabricEntityType.Builder.Living#defaultAttributes(Supplier)}
 		 */
+		@Deprecated
 		public FabricEntityTypeBuilder.Living<T> defaultAttributes(Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder) {
 			Objects.requireNonNull(defaultAttributeBuilder, "Cannot set null attribute builder");
 			this.defaultAttributeBuilder = defaultAttributeBuilder;
 			return this;
 		}
 
+		@Deprecated
 		@Override
 		public EntityType<T> build() {
 			final EntityType<T> type = super.build();
@@ -423,6 +492,7 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 	 *
 	 * @param <T> Entity class.
 	 */
+	@Deprecated
 	public static class Mob<T extends MobEntity> extends FabricEntityTypeBuilder.Living<T> {
 		private SpawnLocation spawnLocation;
 		private Heightmap.Type restrictionHeightmap;
@@ -536,7 +606,9 @@ public class FabricEntityTypeBuilder<T extends Entity> {
 		 * <p>This is used by mobs to determine whether Minecraft should spawn an entity within a certain context.
 		 *
 		 * @return this builder for chaining.
+		 * @deprecated use {@link FabricEntityType.Builder.Mob#spawnRestriction(SpawnRestriction.Location, Heightmap.Type, SpawnRestriction.SpawnPredicate)}
 		 */
+		@Deprecated
 		public FabricEntityTypeBuilder.Mob<T> spawnRestriction(SpawnLocation spawnLocation, Heightmap.Type heightmap, SpawnRestriction.SpawnPredicate<T> spawnPredicate) {
 			this.spawnLocation = Objects.requireNonNull(spawnLocation, "Spawn location cannot be null.");
 			this.restrictionHeightmap = Objects.requireNonNull(heightmap, "Heightmap type cannot be null.");
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityType.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityType.java
deleted file mode 100644
index dde18eec8..000000000
--- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityType.java
+++ /dev/null
@@ -1,44 +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.object.builder;
-
-import com.google.common.collect.ImmutableSet;
-
-import net.minecraft.block.Block;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityDimensions;
-import net.minecraft.entity.EntityType;
-import net.minecraft.entity.SpawnGroup;
-import net.minecraft.resource.featuretoggle.FeatureSet;
-
-public class FabricEntityType<T extends Entity> extends EntityType<T> {
-	private final Boolean alwaysUpdateVelocity;
-
-	public FabricEntityType(EntityType.EntityFactory<T> factory, SpawnGroup spawnGroup, boolean bl, boolean summonable, boolean fireImmune, boolean spawnableFarFromPlayer, ImmutableSet<Block> spawnBlocks, EntityDimensions entityDimensions, float field_50125, int maxTrackDistance, int trackTickInterval, Boolean alwaysUpdateVelocity, FeatureSet featureSet) {
-		super(factory, spawnGroup, bl, summonable, fireImmune, spawnableFarFromPlayer, spawnBlocks, entityDimensions, field_50125, maxTrackDistance, trackTickInterval, featureSet);
-		this.alwaysUpdateVelocity = alwaysUpdateVelocity;
-	}
-
-	@Override
-	public boolean alwaysUpdateVelocity() {
-		if (alwaysUpdateVelocity != null) {
-			return alwaysUpdateVelocity;
-		}
-
-		return super.alwaysUpdateVelocity();
-	}
-}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityTypeImpl.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityTypeImpl.java
new file mode 100644
index 000000000..3a563912b
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricEntityTypeImpl.java
@@ -0,0 +1,106 @@
+/*
+ * 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.object.builder;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.SpawnGroup;
+import net.minecraft.entity.SpawnRestriction;
+import net.minecraft.entity.attribute.DefaultAttributeContainer;
+import net.minecraft.entity.mob.MobEntity;
+import net.minecraft.world.Heightmap;
+
+import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
+import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityType;
+
+public interface FabricEntityTypeImpl {
+	void fabric_setAlwaysUpdateVelocity(Boolean alwaysUpdateVelocity);
+
+	interface Builder {
+		void fabric_setLivingEntityBuilder(Living<? extends LivingEntity> livingBuilder);
+
+		void fabric_setMobEntityBuilder(Mob<? extends MobEntity> mobBuilder);
+
+		static <T extends LivingEntity> EntityType.Builder<T> createLiving(EntityType.EntityFactory<T> factory, SpawnGroup spawnGroup, UnaryOperator<FabricEntityType.Builder.Living<T>> livingBuilder) {
+			EntityType.Builder<T> builder = EntityType.Builder.create(factory, spawnGroup);
+			Living<T> builderImpl = new Living<>();
+			livingBuilder.apply(builderImpl);
+			((Builder) builder).fabric_setLivingEntityBuilder(builderImpl);
+			return builder;
+		}
+
+		static <T extends MobEntity> EntityType.Builder<T> createMob(EntityType.EntityFactory<T> factory, SpawnGroup spawnGroup, UnaryOperator<FabricEntityType.Builder.Mob<T>> mobBuilder) {
+			EntityType.Builder<T> builder = EntityType.Builder.create(factory, spawnGroup);
+			Mob<T> builderImpl = new Mob<>();
+			mobBuilder.apply(builderImpl);
+			((Builder) builder).fabric_setMobEntityBuilder(builderImpl);
+			return builder;
+		}
+
+		sealed class Living<T extends LivingEntity> implements FabricEntityType.Builder.Living<T> permits Mob {
+			@Nullable
+			private Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder;
+
+			@Override
+			public FabricEntityType.Builder.Living<T> defaultAttributes(Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder) {
+				Objects.requireNonNull(defaultAttributeBuilder, "Cannot set null attribute builder");
+				this.defaultAttributeBuilder = defaultAttributeBuilder;
+				return this;
+			}
+
+			public void onBuild(EntityType<T> type) {
+				if (this.defaultAttributeBuilder != null) {
+					FabricDefaultAttributeRegistry.register(type, this.defaultAttributeBuilder.get());
+				}
+			}
+		}
+
+		final class Mob<T extends MobEntity> extends Living<T> implements FabricEntityType.Builder.Mob<T> {
+			private SpawnRestriction.Location restrictionLocation;
+			private Heightmap.Type restrictionHeightmap;
+			private SpawnRestriction.SpawnPredicate<T> spawnPredicate;
+
+			@Override
+			public FabricEntityType.Builder.Mob<T> spawnRestriction(SpawnRestriction.Location location, Heightmap.Type heightmap, SpawnRestriction.SpawnPredicate<T> spawnPredicate) {
+				this.restrictionLocation = Objects.requireNonNull(location, "Location cannot be null.");
+				this.restrictionHeightmap = Objects.requireNonNull(heightmap, "Heightmap type cannot be null.");
+				this.spawnPredicate = Objects.requireNonNull(spawnPredicate, "Spawn predicate cannot be null.");
+				return this;
+			}
+
+			@Override
+			public FabricEntityType.Builder.Mob<T> defaultAttributes(Supplier<DefaultAttributeContainer.Builder> defaultAttributeBuilder) {
+				super.defaultAttributes(defaultAttributeBuilder);
+				return this;
+			}
+
+			public void onBuild(EntityType<T> type) {
+				super.onBuild(type);
+
+				if (this.spawnPredicate != null) {
+					SpawnRestriction.register(type, this.restrictionLocation, this.restrictionHeightmap, this.spawnPredicate);
+				}
+			}
+		}
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/BlockEntityTypeBuilderMixin.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/BlockEntityTypeBuilderMixin.java
new file mode 100644
index 000000000..5d9342ec1
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/BlockEntityTypeBuilderMixin.java
@@ -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.mixin.object.builder;
+
+import com.mojang.datafixers.types.Type;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.block.entity.BlockEntityType;
+
+import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityType;
+
+@Mixin(BlockEntityType.Builder.class)
+public abstract class BlockEntityTypeBuilderMixin<T extends BlockEntity> implements FabricBlockEntityType.Builder<T> {
+	@Shadow
+	public abstract BlockEntityType<T> build(Type<?> type);
+
+	@Override
+	public BlockEntityType<T> build() {
+		return build(null);
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeBuilderMixin.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeBuilderMixin.java
new file mode 100644
index 000000000..286dfdde1
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeBuilderMixin.java
@@ -0,0 +1,114 @@
+/*
+ * 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.object.builder;
+
+import java.util.Objects;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.types.Type;
+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 net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.mob.MobEntity;
+
+import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityType;
+import net.fabricmc.fabric.impl.object.builder.FabricEntityTypeImpl;
+
+@Mixin(EntityType.Builder.class)
+public abstract class EntityTypeBuilderMixin<T extends Entity> implements FabricEntityType.Builder<T>, FabricEntityTypeImpl.Builder {
+	@Shadow
+	public abstract EntityType<T> build(String id);
+
+	@Unique
+	private Boolean alwaysUpdateVelocity = null;
+
+	@Unique
+	private FabricEntityTypeImpl.Builder.Living<? extends LivingEntity> livingBuilder = null;
+	@Unique
+	private FabricEntityTypeImpl.Builder.Mob<? extends MobEntity> mobBuilder = null;
+
+	@Override
+	public EntityType.Builder<T> alwaysUpdateVelocity(boolean forceTrackedVelocityUpdates) {
+		alwaysUpdateVelocity = forceTrackedVelocityUpdates;
+		return (EntityType.Builder<T>) (Object) this;
+	}
+
+	@Override
+	public EntityType<T> build() {
+		return build(null);
+	}
+
+	@Inject(method = "build", at = @At("RETURN"))
+	private void applyChildBuilders(String id, CallbackInfoReturnable<EntityType<T>> cir) {
+		if (!(cir.getReturnValue() instanceof FabricEntityTypeImpl entityType)) {
+			throw new IllegalStateException();
+		}
+
+		entityType.fabric_setAlwaysUpdateVelocity(alwaysUpdateVelocity);
+
+		if (livingBuilder != null) {
+			livingBuilder.onBuild(castLiving(cir.getReturnValue()));
+		}
+
+		if (mobBuilder != null) {
+			mobBuilder.onBuild(castMob(cir.getReturnValue()));
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Unique
+	private static <T extends LivingEntity> EntityType<T> castLiving(EntityType<?> type) {
+		return (EntityType<T>) type;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Unique
+	private static <T extends MobEntity> EntityType<T> castMob(EntityType<?> type) {
+		return (EntityType<T>) type;
+	}
+
+	@WrapOperation(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Util;getChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
+	private @Nullable Type<?> allowNullId(DSL.TypeReference typeReference, String id, Operation<Type<?>> original) {
+		if (id == null) {
+			return null;
+		}
+
+		return original.call(typeReference, id);
+	}
+
+	@Override
+	public void fabric_setLivingEntityBuilder(FabricEntityTypeImpl.Builder.Living<? extends LivingEntity> livingBuilder) {
+		Objects.requireNonNull(livingBuilder, "Cannot set null living entity builder");
+		this.livingBuilder = livingBuilder;
+	}
+
+	@Override
+	public void fabric_setMobEntityBuilder(FabricEntityTypeImpl.Builder.Mob<? extends MobEntity> mobBuilder) {
+		Objects.requireNonNull(mobBuilder, "Cannot set null mob entity builder");
+		this.mobBuilder = mobBuilder;
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeMixin.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeMixin.java
new file mode 100644
index 000000000..4d493d6d6
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/EntityTypeMixin.java
@@ -0,0 +1,45 @@
+/*
+ * 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.object.builder;
+
+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.CallbackInfoReturnable;
+
+import net.minecraft.entity.EntityType;
+
+import net.fabricmc.fabric.impl.object.builder.FabricEntityTypeImpl;
+
+@Mixin(EntityType.class)
+public abstract class EntityTypeMixin implements FabricEntityTypeImpl {
+	@Unique
+	private Boolean alwaysUpdateVelocity;
+
+	@Inject(method = "alwaysUpdateVelocity", at = @At("HEAD"), cancellable = true)
+	public void alwaysUpdateVelocity(CallbackInfoReturnable<Boolean> cir) {
+		if (alwaysUpdateVelocity != null) {
+			cir.setReturnValue(alwaysUpdateVelocity);
+		}
+	}
+
+	@Override
+	public void fabric_setAlwaysUpdateVelocity(Boolean alwaysUpdateVelocity) {
+		this.alwaysUpdateVelocity = alwaysUpdateVelocity;
+	}
+}
diff --git a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
index a5c4f877c..22d799f1e 100644
--- a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
+++ b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
@@ -5,9 +5,12 @@
   "mixins": [
     "AbstractBlockAccessor",
     "AbstractBlockSettingsAccessor",
+    "BlockEntityTypeBuilderMixin",
     "DefaultAttributeRegistryAccessor",
     "DefaultAttributeRegistryMixin",
     "DetectorRailBlockMixin",
+    "EntityTypeBuilderMixin",
+    "EntityTypeMixin",
     "PersistentStateManagerMixin",
     "TradeOffersTypeAwareBuyForOneEmeraldFactoryMixin"
   ],
diff --git a/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json b/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json
index 940c46c6d..8aa8fb234 100644
--- a/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json
@@ -29,6 +29,10 @@
   ],
   "accessWidener" : "fabric-object-builder-api-v1.accesswidener",
   "custom": {
-    "fabric-api:module-lifecycle": "stable"
+    "fabric-api:module-lifecycle": "stable",
+    "loom:injected_interfaces": {
+      "net/minecraft/class_1299\u0024class_1300": ["net/fabricmc/fabric/api/object/builder/v1/entity/FabricEntityType\u0024Builder<TT;>"],
+      "net/minecraft/class_2591\u0024class_2592": ["net/fabricmc/fabric/api/object/builder/v1/block/entity/FabricBlockEntityType\u0024Builder<TT;>"]
+    }
   }
 }
diff --git a/fabric-object-builder-api-v1/src/test/java/net/fabricmc/fabric/test/object/builder/FabricEntityTypeTest.java b/fabric-object-builder-api-v1/src/test/java/net/fabricmc/fabric/test/object/builder/FabricEntityTypeTest.java
new file mode 100644
index 000000000..f3f477518
--- /dev/null
+++ b/fabric-object-builder-api-v1/src/test/java/net/fabricmc/fabric/test/object/builder/FabricEntityTypeTest.java
@@ -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.test.object.builder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import net.minecraft.Bootstrap;
+import net.minecraft.SharedConstants;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.SpawnGroup;
+import net.minecraft.entity.SpawnRestriction;
+import net.minecraft.entity.attribute.DefaultAttributeContainer;
+import net.minecraft.entity.attribute.DefaultAttributeRegistry;
+import net.minecraft.entity.attribute.EntityAttributes;
+import net.minecraft.entity.mob.MobEntity;
+import net.minecraft.entity.passive.PigEntity;
+import net.minecraft.world.Heightmap;
+
+import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityType;
+
+public class FabricEntityTypeTest {
+	@BeforeAll
+	static void beforeAll() {
+		SharedConstants.createGameVersion();
+		Bootstrap.initialize();
+	}
+
+	@Test
+	void buildEntityType() {
+		EntityType<Entity> type = EntityType.Builder.create(SpawnGroup.MISC)
+				.alwaysUpdateVelocity(true)
+				.build();
+
+		assertNotNull(type);
+		assertTrue(type.alwaysUpdateVelocity());
+	}
+
+	@Test
+	void buildLivingEntityType() {
+		EntityType<LivingEntity> type = FabricEntityType.Builder.createLiving((t, w) -> null, SpawnGroup.MISC, living -> living
+						.defaultAttributes(FabricEntityTypeTest::createAttributes)
+		).build();
+
+		assertNotNull(type);
+		assertNotNull(DefaultAttributeRegistry.get(type));
+	}
+
+	@Test
+	void buildMobEntityType() {
+		EntityType<MobEntity> type = FabricEntityType.Builder.createMob((t, w) -> null, SpawnGroup.MISC, mob -> mob
+				.spawnRestriction(SpawnRestriction.Location.ON_GROUND, Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, PigEntity::canMobSpawn)
+				.defaultAttributes(FabricEntityTypeTest::createAttributes)
+		).build();
+
+		assertNotNull(type);
+		assertEquals(SpawnRestriction.Location.ON_GROUND, SpawnRestriction.getLocation(type));
+		assertNotNull(DefaultAttributeRegistry.get(type));
+	}
+
+	private static DefaultAttributeContainer.Builder createAttributes() {
+		return MobEntity.createMobAttributes()
+				.add(EntityAttributes.GENERIC_MAX_HEALTH, 10.0)
+				.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.25);
+	}
+}