--- a/net/minecraft/entity/EntityType.java
+++ b/net/minecraft/entity/EntityType.java
@@ -143,7 +143,7 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-public class EntityType<T extends Entity> {
+public class EntityType<T extends Entity> extends net.minecraftforge.registries.ForgeRegistryEntry<EntityType<?>> {
    private static final Logger field_200731_aJ = LogManager.getLogger();
    public static final EntityType<AreaEffectCloudEntity> field_200788_b = func_200712_a("area_effect_cloud", EntityType.Builder.<AreaEffectCloudEntity>func_220322_a(AreaEffectCloudEntity::new, EntityClassification.MISC).func_220320_c().func_220321_a(6.0F, 0.5F).func_233606_a_(10).func_233608_b_(Integer.MAX_VALUE));
    public static final EntityType<ArmorStandEntity> field_200789_c = func_200712_a("armor_stand", EntityType.Builder.<ArmorStandEntity>func_220322_a(ArmorStandEntity::new, EntityClassification.MISC).func_220321_a(0.5F, 1.975F).func_233606_a_(10));
@@ -270,6 +270,12 @@
    private ResourceLocation field_220358_bg;
    private final EntitySize field_220359_bi;
 
+   private final java.util.function.Predicate<EntityType<?>> velocityUpdateSupplier;
+   private final java.util.function.ToIntFunction<EntityType<?>> trackingRangeSupplier;
+   private final java.util.function.ToIntFunction<EntityType<?>> updateIntervalSupplier;
+   private final java.util.function.BiFunction<net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity, World, T> customClientFactory;
+   private final net.minecraftforge.common.util.ReverseTagWrapper<EntityType<?>> reverseTags = new net.minecraftforge.common.util.ReverseTagWrapper<>(this, net.minecraft.tags.EntityTypeTags::func_219762_a);
+
    private static <T extends Entity> EntityType<T> func_200712_a(String p_200712_0_, EntityType.Builder<T> p_200712_1_) {
       return Registry.func_218325_a(Registry.field_212629_r, p_200712_0_, p_200712_1_.func_206830_a(p_200712_0_));
    }
@@ -283,6 +289,9 @@
    }
 
    public EntityType(EntityType.IFactory<T> p_i231489_1_, EntityClassification p_i231489_2_, boolean p_i231489_3_, boolean p_i231489_4_, boolean p_i231489_5_, boolean p_i231489_6_, ImmutableSet<Block> p_i231489_7_, EntitySize p_i231489_8_, int p_i231489_9_, int p_i231489_10_) {
+       this(p_i231489_1_, p_i231489_2_, p_i231489_3_, p_i231489_4_, p_i231489_5_, p_i231489_6_, p_i231489_7_, p_i231489_8_, p_i231489_9_, p_i231489_10_, EntityType::defaultVelocitySupplier, EntityType::defaultTrackingRangeSupplier, EntityType::defaultUpdateIntervalSupplier, null);
+   }
+   public EntityType(EntityType.IFactory<T> p_i231489_1_, EntityClassification p_i231489_2_, boolean p_i231489_3_, boolean p_i231489_4_, boolean p_i231489_5_, boolean p_i231489_6_, ImmutableSet<Block> p_i231489_7_, EntitySize p_i231489_8_, int p_i231489_9_, int p_i231489_10_, final java.util.function.Predicate<EntityType<?>> velocityUpdateSupplier, final java.util.function.ToIntFunction<EntityType<?>> trackingRangeSupplier, final java.util.function.ToIntFunction<EntityType<?>> updateIntervalSupplier, final java.util.function.BiFunction<net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity, World, T> customClientFactory) {
       this.field_200732_aK = p_i231489_1_;
       this.field_220355_ba = p_i231489_2_;
       this.field_225438_be = p_i231489_6_;
@@ -293,6 +302,10 @@
       this.field_220359_bi = p_i231489_8_;
       this.field_233594_bl_ = p_i231489_9_;
       this.field_233595_bm_ = p_i231489_10_;
+      this.velocityUpdateSupplier = velocityUpdateSupplier;
+      this.trackingRangeSupplier = trackingRangeSupplier;
+      this.updateIntervalSupplier = updateIntervalSupplier;
+      this.customClientFactory = customClientFactory;
    }
 
    @Nullable
@@ -304,6 +317,7 @@
    public T func_220342_a(ServerWorld p_220342_1_, @Nullable CompoundNBT p_220342_2_, @Nullable ITextComponent p_220342_3_, @Nullable PlayerEntity p_220342_4_, BlockPos p_220342_5_, SpawnReason p_220342_6_, boolean p_220342_7_, boolean p_220342_8_) {
       T t = this.func_220349_b(p_220342_1_, p_220342_2_, p_220342_3_, p_220342_4_, p_220342_5_, p_220342_6_, p_220342_7_, p_220342_8_);
       if (t != null) {
+         if (t instanceof net.minecraft.entity.MobEntity && net.minecraftforge.event.ForgeEventFactory.doSpecialSpawn((net.minecraft.entity.MobEntity) t, p_220342_1_, p_220342_5_.func_177958_n(), p_220342_5_.func_177956_o(), p_220342_5_.func_177952_p(), null, p_220342_6_)) return null;
          p_220342_1_.func_242417_l(t);
       }
 
@@ -504,14 +518,23 @@
    }
 
    public int func_233602_m_() {
+      return trackingRangeSupplier.applyAsInt(this);
+   }
+   private int defaultTrackingRangeSupplier() {
       return this.field_233594_bl_;
    }
 
    public int func_220332_l() {
+      return updateIntervalSupplier.applyAsInt(this);
+   }
+   private int defaultUpdateIntervalSupplier() {
       return this.field_233595_bm_;
    }
 
    public boolean func_220340_m() {
+      return velocityUpdateSupplier.test(this);
+   }
+   private boolean defaultVelocitySupplier() {
       return this != field_200729_aH && this != field_200770_J && this != field_200760_az && this != field_200791_e && this != field_200766_F && this != field_200768_H && this != field_200782_V && this != field_200801_o && this != field_200805_s;
    }
 
@@ -519,6 +542,19 @@
       return p_220341_1_.func_230235_a_(this);
    }
 
+   public T customClientSpawn(net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity packet, World world) {
+       if (customClientFactory == null) return this.func_200721_a(world);
+       return customClientFactory.apply(packet, world);
+   }
+
+   /**
+    * Retrieves a list of tags names this is known to be associated with.
+    * This should be used in favor of TagCollection.getOwningTags, as this caches the result and automatically updates when the TagCollection changes.
+    */
+   public java.util.Set<ResourceLocation> getTags() {
+       return reverseTags.getTagNames();
+   }
+
    public static class Builder<T extends Entity> {
       private final EntityType.IFactory<T> field_200709_a;
       private final EntityClassification field_220324_b;
@@ -531,6 +567,11 @@
       private int field_233605_i_ = 3;
       private EntitySize field_220326_f = EntitySize.func_220314_b(0.6F, 1.8F);
 
+      private java.util.function.Predicate<EntityType<?>> velocityUpdateSupplier = EntityType::defaultVelocitySupplier;
+      private java.util.function.ToIntFunction<EntityType<?>> trackingRangeSupplier = EntityType::defaultTrackingRangeSupplier;
+      private java.util.function.ToIntFunction<EntityType<?>> updateIntervalSupplier = EntityType::defaultUpdateIntervalSupplier;
+      private java.util.function.BiFunction<net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity, World, T> customClientFactory;
+
       private Builder(EntityType.IFactory<T> p_i50479_1_, EntityClassification p_i50479_2_) {
          this.field_200709_a = p_i50479_1_;
          this.field_220324_b = p_i50479_2_;
@@ -587,12 +628,36 @@
          return this;
       }
 
+      public EntityType.Builder<T> setUpdateInterval(int interval) {
+          this.updateIntervalSupplier = t->interval;
+          return this;
+      }
+
+      public EntityType.Builder<T> setTrackingRange(int range) {
+          this.trackingRangeSupplier = t->range;
+          return this;
+      }
+
+      public EntityType.Builder<T> setShouldReceiveVelocityUpdates(boolean value) {
+          this.velocityUpdateSupplier = t->value;
+          return this;
+      }
+
+      /**
+       * By default, entities are spawned clientside via {@link EntityType#create(World)}.
+       * If you need finer control over the spawning process, use this to get read access to the spawn packet.
+       */
+      public EntityType.Builder<T> setCustomClientFactory(java.util.function.BiFunction<net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity, World, T> customClientFactory) {
+          this.customClientFactory = customClientFactory;
+          return this;
+      }
+
       public EntityType<T> func_206830_a(String p_206830_1_) {
          if (this.field_200710_b) {
             Util.func_240976_a_(TypeReferences.field_211298_n, p_206830_1_);
          }
 
-         return new EntityType<>(this.field_200709_a, this.field_220324_b, this.field_200710_b, this.field_200711_c, this.field_220325_e, this.field_225436_f, this.field_233603_c_, this.field_220326_f, this.field_233604_h_, this.field_233605_i_);
+         return new EntityType<>(this.field_200709_a, this.field_220324_b, this.field_200710_b, this.field_200711_c, this.field_220325_e, this.field_225436_f, this.field_233603_c_, this.field_220326_f, this.field_233604_h_, this.field_233605_i_, velocityUpdateSupplier, trackingRangeSupplier, updateIntervalSupplier, customClientFactory);
       }
    }
 
