--- a/net/minecraft/tags/TagRegistry.java
+++ b/net/minecraft/tags/TagRegistry.java
@@ -16,13 +16,51 @@
    private ITagCollection<T> field_232930_b_ = ITagCollection.func_242205_c();
    private final List<TagRegistry.NamedTag<T>> field_232931_c_ = Lists.newArrayList();
    private final Function<ITagCollectionSupplier, ITagCollection<T>> field_242184_c;
+   private static java.util.Map<ResourceLocation, List<TagRegistry.NamedTag<?>>> toAdd = com.google.common.collect.Maps.newHashMap();
 
    public TagRegistry(Function<ITagCollectionSupplier, ITagCollection<T>> p_i241894_1_) {
       this.field_242184_c = p_i241894_1_;
    }
 
    public ITag.INamedTag<T> func_232937_a_(String p_232937_1_) {
-      TagRegistry.NamedTag<T> namedtag = new TagRegistry.NamedTag<>(new ResourceLocation(p_232937_1_));
+       return add(new TagRegistry.NamedTag<>(new ResourceLocation(p_232937_1_)));
+   }
+
+   public net.minecraftforge.common.Tags.IOptionalNamedTag<T> createOptional(ResourceLocation key, @Nullable Set<java.util.function.Supplier<T>> defaults) {
+       return add(new TagRegistry.OptionalNamedTag<>(key, defaults));
+   }
+
+   /** Call via ForgeTagHandler#makeWrapperTag to avoid any exceptions due to calling this after it is safe to call {@link #createTag(String)} */
+   public static <T> ITag.INamedTag<T> createDelayedTag(ResourceLocation tagRegistry, ResourceLocation name) {
+      return delayedAdd(tagRegistry, new TagRegistry.NamedTag<>(name));
+   }
+
+   /** Call via ForgeTagHandler#createOptionalTag to avoid any exceptions due to calling this after it is safe to call {@link #createOptional(ResourceLocation, Set)} */
+   public static <T> net.minecraftforge.common.Tags.IOptionalNamedTag<T> createDelayedOptional(ResourceLocation tagRegistry, ResourceLocation key, @Nullable Set<java.util.function.Supplier<T>> defaults) {
+      return delayedAdd(tagRegistry, new TagRegistry.OptionalNamedTag<>(key, defaults));
+   }
+
+   private static synchronized <T, R extends TagRegistry.NamedTag<T>> R delayedAdd(ResourceLocation tagRegistry, R tag) {
+      if (toAdd == null) throw new RuntimeException("Creating delayed tags or optional tags, is only supported before custom tag types have been added.");
+      toAdd.computeIfAbsent(tagRegistry, registry -> Lists.newArrayList()).add(tag);
+      return tag;
+   }
+
+   public static void performDelayedAdd() {
+      if (toAdd != null) {
+         for (java.util.Map.Entry<ResourceLocation, List<TagRegistry.NamedTag<?>>> entry : toAdd.entrySet()) {
+            TagRegistry<?> tagRegistry = TagRegistryManager.get(entry.getKey());
+            if (tagRegistry == null) throw new RuntimeException("A mod attempted to add a delayed tag for a registry that doesn't have custom tag support.");
+            for (TagRegistry.NamedTag<?> tag : entry.getValue()) {
+               tagRegistry.add((TagRegistry.NamedTag) tag);
+            }
+         }
+         toAdd = null;
+      }
+   }
+
+   private <R extends TagRegistry.NamedTag<T>> R add(R namedtag) {
+      namedtag.func_232943_a_(field_232930_b_::func_199910_a);
       this.field_232931_c_.add(namedtag);
       return namedtag;
    }
@@ -46,6 +84,20 @@
       });
    }
 
+   public ITagCollection<T> reinjectOptionalTags(ITagCollection<T> tagCollection) {
+      java.util.Map<ResourceLocation, ITag<T>> currentTags = tagCollection.func_241833_a();
+      java.util.Map<ResourceLocation, ITag<T>> missingOptionals = this.field_232931_c_.stream().filter(e -> e instanceof OptionalNamedTag && !currentTags.containsKey(e.func_230234_a_())).collect(Collectors.toMap(NamedTag::func_230234_a_, namedTag -> {
+         OptionalNamedTag<T> optionalNamedTag = (OptionalNamedTag<T>) namedTag;
+         optionalNamedTag.defaulted = true;
+         return optionalNamedTag.resolveDefaulted();
+      }));
+      if (!missingOptionals.isEmpty()) {
+         missingOptionals.putAll(currentTags);
+         return ITagCollection.func_242202_a(missingOptionals);
+      }
+      return tagCollection;
+   }
+
    public ITagCollection<T> func_232939_b_() {
       return this.field_232930_b_;
    }
@@ -56,7 +108,7 @@
 
    public Set<ResourceLocation> func_242189_b(ITagCollectionSupplier p_242189_1_) {
       ITagCollection<T> itagcollection = this.field_242184_c.apply(p_242189_1_);
-      Set<ResourceLocation> set = this.field_232931_c_.stream().map(TagRegistry.NamedTag::func_230234_a_).collect(Collectors.toSet());
+      Set<ResourceLocation> set = this.field_232931_c_.stream().filter(e -> !(e instanceof OptionalNamedTag)).map(TagRegistry.NamedTag::func_230234_a_).collect(Collectors.toSet());
       ImmutableSet<ResourceLocation> immutableset = ImmutableSet.copyOf(itagcollection.func_199908_a());
       return Sets.difference(set, immutableset);
    }
@@ -93,5 +145,40 @@
       public List<T> func_230236_b_() {
          return this.func_232944_c_().func_230236_b_();
       }
+
+      @Override
+      public String toString() {
+          return "NamedTag[" + func_230234_a_().toString() + ']';
+      }
+      @Override public boolean equals(Object o) { return (o == this) || (o instanceof ITag.INamedTag && java.util.Objects.equals(this.func_230234_a_(), ((ITag.INamedTag<T>)o).func_230234_a_())); }
+      @Override public int hashCode() { return func_230234_a_().hashCode(); }
    }
+
+   private static class OptionalNamedTag<T> extends NamedTag<T> implements net.minecraftforge.common.Tags.IOptionalNamedTag<T> {
+      @Nullable
+      private final Set<java.util.function.Supplier<T>> defaults;
+      private boolean defaulted = false;
+
+      private OptionalNamedTag(ResourceLocation name, @Nullable Set<java.util.function.Supplier<T>> defaults) {
+         super(name);
+         this.defaults = defaults;
+      }
+
+      @Override
+      public boolean isDefaulted() {
+         return defaulted;
+      }
+
+      Tag<T> resolveDefaulted() {
+         if (defaults == null || defaults.isEmpty()) {
+            return Tag.func_241284_a_();
+         }
+         return Tag.func_241286_a_(ImmutableSet.copyOf(defaults.stream().map(java.util.function.Supplier::get).collect(Collectors.toSet())));
+      }
+
+      @Override
+      public String toString() {
+          return "OptionalNamedTag[" + func_230234_a_().toString() + ']';
+      }
+   }
 }
