diff --git a/build.gradle b/build.gradle index cb07cdc5a..4c8c932e1 100644 --- a/build.gradle +++ b/build.gradle @@ -352,6 +352,7 @@ project(':forge') { '--mod', 'piston_event_test', '--mod', 'global_loot_test', '--mod', 'scaffolding_test', + '--mod', 'custom_tag_types_test', '--output', rootProject.file('src/generated_test/resources/'), '--existing', sourceSets.main.resources.srcDirs[0] } diff --git a/patches/minecraft/net/minecraft/client/network/play/ClientPlayNetHandler.java.patch b/patches/minecraft/net/minecraft/client/network/play/ClientPlayNetHandler.java.patch index e4cccc701..02491b069 100644 --- a/patches/minecraft/net/minecraft/client/network/play/ClientPlayNetHandler.java.patch +++ b/patches/minecraft/net/minecraft/client/network/play/ClientPlayNetHandler.java.patch @@ -77,7 +77,21 @@ if (effect != null) { EffectInstance effectinstance = new EffectInstance(effect, p_147260_1_.func_180755_e(), p_147260_1_.func_149428_f(), p_147260_1_.func_186984_g(), p_147260_1_.func_179707_f(), p_147260_1_.func_205527_h()); effectinstance.func_100012_b(p_147260_1_.func_149429_c()); -@@ -1883,10 +1896,12 @@ +@@ -1388,11 +1401,12 @@ + public void func_199723_a(STagsListPacket p_199723_1_) { + PacketThreadUtil.func_218797_a(p_199723_1_, this, this.field_147299_f); + ITagCollectionSupplier itagcollectionsupplier = p_199723_1_.func_199858_a(); +- Multimap multimap = TagRegistryManager.func_242198_b(itagcollectionsupplier); ++ Multimap multimap = TagRegistryManager.validateVanillaTags(itagcollectionsupplier); + if (!multimap.isEmpty()) { + field_147301_d.warn("Incomplete server tags, disconnecting. Missing: {}", (Object)multimap); + this.field_147302_e.func_150718_a(new TranslationTextComponent("multiplayer.disconnect.missing_tags")); + } else { ++ net.minecraftforge.common.ForgeTagHandler.resetCachedTagCollections(); + this.field_199725_m = itagcollectionsupplier; + if (!this.field_147302_e.func_150731_c()) { + itagcollectionsupplier.func_242212_e(); +@@ -1883,10 +1897,12 @@ int l5 = packetbuffer.readInt(); this.field_147299_f.field_184132_p.field_229018_q_.func_229022_a_(blockpos8, l3, s10, l5); } else { diff --git a/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch b/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch index fd74a742b..23b23743d 100644 --- a/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch +++ b/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch @@ -16,7 +16,15 @@ GameRules gamerules = serverworld1.func_82736_K(); boolean flag = gamerules.func_223586_b(GameRules.field_226683_z_); boolean flag1 = gamerules.func_223586_b(GameRules.field_223612_o); -@@ -170,7 +172,7 @@ +@@ -156,6 +158,7 @@ + serverplaynethandler.func_147359_a(new SHeldItemChangePacket(p_72355_2_.field_71071_by.field_70461_c)); + serverplaynethandler.func_147359_a(new SUpdateRecipesPacket(this.field_72400_f.func_199529_aN().func_199510_b())); + serverplaynethandler.func_147359_a(new STagsListPacket(this.field_72400_f.func_244266_aF())); ++ net.minecraftforge.fml.network.NetworkHooks.syncCustomTagTypes(p_72355_2_, this.field_72400_f.func_244266_aF()); + this.func_187243_f(p_72355_2_); + p_72355_2_.func_147099_x().func_150877_d(); + p_72355_2_.func_192037_E().func_192826_c(p_72355_2_); +@@ -170,7 +173,7 @@ this.func_232641_a_(iformattabletextcomponent.func_240699_a_(TextFormatting.YELLOW), ChatType.SYSTEM, Util.field_240973_b_); serverplaynethandler.func_147364_a(p_72355_2_.func_226277_ct_(), p_72355_2_.func_226278_cu_(), p_72355_2_.func_226281_cx_(), p_72355_2_.field_70177_z, p_72355_2_.field_70125_A); @@ -25,7 +33,7 @@ this.field_177454_f.put(p_72355_2_.func_110124_au(), p_72355_2_); this.func_148540_a(new SPlayerListItemPacket(SPlayerListItemPacket.Action.ADD_PLAYER, p_72355_2_)); -@@ -225,6 +227,7 @@ +@@ -225,6 +228,7 @@ } p_72355_2_.func_71116_b(); @@ -33,7 +41,7 @@ } protected void func_96456_a(ServerScoreboard p_96456_1_, ServerPlayerEntity p_96456_2_) { -@@ -285,6 +288,7 @@ +@@ -285,6 +289,7 @@ compoundnbt1 = compoundnbt; p_72380_1_.func_70020_e(compoundnbt); field_148546_d.debug("loading single player"); @@ -41,7 +49,7 @@ } else { compoundnbt1 = this.field_72412_k.func_237336_b_(p_72380_1_); } -@@ -293,6 +297,7 @@ +@@ -293,6 +298,7 @@ } protected void func_72391_b(ServerPlayerEntity p_72391_1_) { @@ -49,7 +57,7 @@ this.field_72412_k.func_237335_a_(p_72391_1_); ServerStatisticsManager serverstatisticsmanager = this.field_148547_k.get(p_72391_1_.func_110124_au()); if (serverstatisticsmanager != null) { -@@ -307,6 +312,7 @@ +@@ -307,6 +313,7 @@ } public void func_72367_e(ServerPlayerEntity p_72367_1_) { @@ -57,7 +65,7 @@ ServerWorld serverworld = p_72367_1_.func_71121_q(); p_72367_1_.func_195066_a(Stats.field_75947_j); this.func_72391_b(p_72367_1_); -@@ -330,7 +336,7 @@ +@@ -330,7 +337,7 @@ p_72367_1_.func_213319_R(); serverworld.func_217434_e(p_72367_1_); p_72367_1_.func_192039_O().func_192745_a(); @@ -66,7 +74,7 @@ this.field_72400_f.func_201300_aS().func_201382_b(p_72367_1_); UUID uuid = p_72367_1_.func_110124_au(); ServerPlayerEntity serverplayerentity = this.field_177454_f.get(uuid); -@@ -400,8 +406,8 @@ +@@ -400,8 +407,8 @@ } public ServerPlayerEntity func_232644_a_(ServerPlayerEntity p_232644_1_, boolean p_232644_2_) { @@ -77,7 +85,7 @@ BlockPos blockpos = p_232644_1_.func_241140_K_(); float f = p_232644_1_.func_242109_L(); boolean flag = p_232644_1_.func_241142_M_(); -@@ -424,6 +430,7 @@ +@@ -424,6 +431,7 @@ ServerPlayerEntity serverplayerentity = new ServerPlayerEntity(this.field_72400_f, serverworld1, p_232644_1_.func_146103_bH(), playerinteractionmanager); serverplayerentity.field_71135_a = p_232644_1_.field_71135_a; serverplayerentity.func_193104_a(p_232644_1_, p_232644_2_); @@ -85,7 +93,7 @@ serverplayerentity.func_145769_d(p_232644_1_.func_145782_y()); serverplayerentity.func_184819_a(p_232644_1_.func_184591_cq()); -@@ -465,10 +472,11 @@ +@@ -465,10 +473,11 @@ this.func_72354_b(serverplayerentity, serverworld1); this.func_187243_f(serverplayerentity); serverworld1.func_217433_d(serverplayerentity); @@ -98,7 +106,7 @@ if (flag2) { serverplayerentity.field_71135_a.func_147359_a(new SPlaySoundEffectPacket(SoundEvents.field_232818_ms_, SoundCategory.BLOCKS, (double)blockpos.func_177958_n(), (double)blockpos.func_177956_o(), (double)blockpos.func_177952_p(), 1.0F, 1.0F)); } -@@ -786,7 +794,7 @@ +@@ -786,7 +795,7 @@ } public List func_181057_v() { @@ -107,7 +115,15 @@ } @Nullable -@@ -816,4 +824,12 @@ +@@ -804,6 +813,7 @@ + } + + this.func_148540_a(new STagsListPacket(this.field_72400_f.func_244266_aF())); ++ net.minecraftforge.fml.network.NetworkHooks.syncCustomTagTypes(this.field_72400_f.func_244266_aF()); + SUpdateRecipesPacket supdaterecipespacket = new SUpdateRecipesPacket(this.field_72400_f.func_199529_aN().func_199510_b()); + + for(ServerPlayerEntity serverplayerentity : this.field_72404_b) { +@@ -816,4 +826,12 @@ public boolean func_206257_x() { return this.field_72407_n; } diff --git a/patches/minecraft/net/minecraft/tags/ITagCollectionSupplier.java.patch b/patches/minecraft/net/minecraft/tags/ITagCollectionSupplier.java.patch index d688b2db0..cb6c898e9 100644 --- a/patches/minecraft/net/minecraft/tags/ITagCollectionSupplier.java.patch +++ b/patches/minecraft/net/minecraft/tags/ITagCollectionSupplier.java.patch @@ -1,10 +1,19 @@ --- a/net/minecraft/tags/ITagCollectionSupplier.java +++ b/net/minecraft/tags/ITagCollectionSupplier.java +@@ -8,7 +8,7 @@ + import net.minecraft.network.PacketBuffer; + import net.minecraft.util.registry.Registry; + +-public interface ITagCollectionSupplier { ++public interface ITagCollectionSupplier extends net.minecraftforge.common.extensions.IForgeTagCollectionSupplier { + ITagCollectionSupplier field_242208_a = func_242209_a(ITagCollection.func_242205_c(), ITagCollection.func_242205_c(), ITagCollection.func_242205_c(), ITagCollection.func_242205_c()); + + ITagCollection func_241835_a(); @@ -22,6 +22,7 @@ default void func_242212_e() { TagRegistryManager.func_242193_a(this); Blocks.func_235419_a_(); -+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.TagsUpdatedEvent(this)); ++ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.TagsUpdatedEvent.VanillaTagTypes(this)); } default void func_242210_a(PacketBuffer p_242210_1_) { diff --git a/patches/minecraft/net/minecraft/tags/NetworkTagManager.java.patch b/patches/minecraft/net/minecraft/tags/NetworkTagManager.java.patch new file mode 100644 index 000000000..7229ecbf6 --- /dev/null +++ b/patches/minecraft/net/minecraft/tags/NetworkTagManager.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/tags/NetworkTagManager.java ++++ b/net/minecraft/tags/NetworkTagManager.java +@@ -20,6 +20,7 @@ + protected TagCollectionReader field_199720_b = new TagCollectionReader<>(Registry.field_212630_s::func_241873_b, "tags/items", "item"); + protected TagCollectionReader field_205705_c = new TagCollectionReader<>(Registry.field_212619_h::func_241873_b, "tags/fluids", "fluid"); + protected TagCollectionReader> field_215299_d = new TagCollectionReader<>(Registry.field_212629_r::func_241873_b, "tags/entity_types", "entity_type"); ++ protected Map> customTagTypeReaders = net.minecraftforge.common.ForgeTagHandler.createCustomTagTypeReaders(); + private ITagCollectionSupplier field_242230_e = ITagCollectionSupplier.field_242208_a; + + public ITagCollectionSupplier func_242231_a() { +@@ -31,11 +32,13 @@ + CompletableFuture> completablefuture1 = this.field_199720_b.func_242224_a(p_215226_2_, p_215226_5_); + CompletableFuture> completablefuture2 = this.field_205705_c.func_242224_a(p_215226_2_, p_215226_5_); + CompletableFuture> completablefuture3 = this.field_215299_d.func_242224_a(p_215226_2_, p_215226_5_); +- return CompletableFuture.allOf(completablefuture, completablefuture1, completablefuture2, completablefuture3).thenCompose(p_215226_1_::func_216872_a).thenAcceptAsync((p_232979_5_) -> { ++ CompletableFuture> customTagTypeResults = net.minecraftforge.common.ForgeTagHandler.getCustomTagTypeReloadResults(p_215226_2_, p_215226_5_, customTagTypeReaders); ++ return CompletableFuture.allOf(completablefuture, completablefuture1, completablefuture2, completablefuture3, customTagTypeResults).thenCompose(p_215226_1_::func_216872_a).thenAcceptAsync((p_232979_5_) -> { + ITagCollection itagcollection = this.field_199719_a.func_242226_a(completablefuture.join()); + ITagCollection itagcollection1 = this.field_199720_b.func_242226_a(completablefuture1.join()); + ITagCollection itagcollection2 = this.field_205705_c.func_242226_a(completablefuture2.join()); + ITagCollection> itagcollection3 = this.field_215299_d.func_242226_a(completablefuture3.join()); ++ net.minecraftforge.common.ForgeTagHandler.updateCustomTagTypes(customTagTypeResults.join()); + ITagCollectionSupplier itagcollectionsupplier = ITagCollectionSupplier.func_242209_a(itagcollection, itagcollection1, itagcollection2, itagcollection3); + Multimap multimap = TagRegistryManager.func_242198_b(itagcollectionsupplier); + if (!multimap.isEmpty()) { diff --git a/patches/minecraft/net/minecraft/tags/TagCollectionManager.java.patch b/patches/minecraft/net/minecraft/tags/TagCollectionManager.java.patch index bbacc1f1a..6867f87e9 100644 --- a/patches/minecraft/net/minecraft/tags/TagCollectionManager.java.patch +++ b/patches/minecraft/net/minecraft/tags/TagCollectionManager.java.patch @@ -5,7 +5,7 @@ public class TagCollectionManager { - private static volatile ITagCollectionSupplier field_232918_a_ = ITagCollectionSupplier.func_242209_a(ITagCollection.func_242202_a(BlockTags.func_242174_b().stream().collect(Collectors.toMap(ITag.INamedTag::func_230234_a_, (p_242183_0_) -> { -+ private static volatile ITagCollectionSupplier field_232918_a_ = ITagCollectionSupplier.func_242209_a(ITagCollection.func_242202_a(BlockTags.func_242174_b().stream().distinct().collect(Collectors.toMap(ITag.INamedTag::func_230234_a_, (p_242183_0_) -> { ++ private static volatile ITagCollectionSupplier field_232918_a_ = net.minecraftforge.common.ForgeTagHandler.populateTagCollectionManager(ITagCollection.func_242202_a(BlockTags.func_242174_b().stream().distinct().collect(Collectors.toMap(ITag.INamedTag::func_230234_a_, (p_242183_0_) -> { return p_242183_0_; - }))), ITagCollection.func_242202_a(ItemTags.func_242177_b().stream().collect(Collectors.toMap(ITag.INamedTag::func_230234_a_, (p_242182_0_) -> { + }))), ITagCollection.func_242202_a(ItemTags.func_242177_b().stream().distinct().collect(Collectors.toMap(ITag.INamedTag::func_230234_a_, (p_242182_0_) -> { diff --git a/patches/minecraft/net/minecraft/tags/TagRegistry.java.patch b/patches/minecraft/net/minecraft/tags/TagRegistry.java.patch index 81239d268..160a9d483 100644 --- a/patches/minecraft/net/minecraft/tags/TagRegistry.java.patch +++ b/patches/minecraft/net/minecraft/tags/TagRegistry.java.patch @@ -1,6 +1,13 @@ --- a/net/minecraft/tags/TagRegistry.java +++ b/net/minecraft/tags/TagRegistry.java -@@ -22,7 +22,15 @@ +@@ -16,13 +16,51 @@ + private ITagCollection field_232930_b_ = ITagCollection.func_242205_c(); + private final List> field_232931_c_ = Lists.newArrayList(); + private final Function> field_242184_c; ++ private static java.util.Map>> toAdd = com.google.common.collect.Maps.newHashMap(); + + public TagRegistry(Function> p_i241894_1_) { + this.field_242184_c = p_i241894_1_; } public ITag.INamedTag func_232937_a_(String p_232937_1_) { @@ -12,12 +19,41 @@ + 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 #func_232937_a_(String)} */ ++ public static ITag.INamedTag 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, java.util.function.Supplier)} */ ++ public static net.minecraftforge.common.Tags.IOptionalNamedTag createDelayedOptional(ResourceLocation tagRegistry, ResourceLocation key, @Nullable java.util.function.Supplier> defaults) { ++ return delayedAdd(tagRegistry, new TagRegistry.OptionalNamedTag<>(key, defaults)); ++ } ++ ++ private static synchronized > 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>> 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 add(R namedtag) { + namedtag.func_232943_a_(field_232930_b_::func_199910_a); this.field_232931_c_.add(namedtag); return namedtag; } -@@ -56,7 +64,7 @@ +@@ -56,7 +94,7 @@ public Set func_242189_b(ITagCollectionSupplier p_242189_1_) { ITagCollection itagcollection = this.field_242184_c.apply(p_242189_1_); @@ -26,7 +62,7 @@ ImmutableSet immutableset = ImmutableSet.copyOf(itagcollection.func_199908_a()); return Sets.difference(set, immutableset); } -@@ -93,5 +101,45 @@ +@@ -93,5 +131,45 @@ public List func_230236_b_() { return this.func_232944_c_().func_230236_b_(); } diff --git a/patches/minecraft/net/minecraft/tags/TagRegistryManager.java.patch b/patches/minecraft/net/minecraft/tags/TagRegistryManager.java.patch new file mode 100644 index 000000000..0e234ed5c --- /dev/null +++ b/patches/minecraft/net/minecraft/tags/TagRegistryManager.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/tags/TagRegistryManager.java ++++ b/net/minecraft/tags/TagRegistryManager.java +@@ -51,4 +51,23 @@ + throw new IllegalStateException("Missing helper registrations"); + } + } ++ ++ @javax.annotation.Nullable ++ public static TagRegistry get(ResourceLocation rl) { ++ return field_242190_a.get(rl); ++ } ++ ++ public static Multimap validateVanillaTags(ITagCollectionSupplier tagCollectionSupplier) { ++ Multimap missingTags = HashMultimap.create(); ++ for (java.util.Map.Entry> entry : field_242190_a.entrySet()) { ++ if (!net.minecraftforge.common.ForgeTagHandler.getCustomTagTypeNames().contains(entry.getKey())) { ++ missingTags.putAll(entry.getKey(), entry.getValue().func_242189_b(tagCollectionSupplier)); ++ } ++ } ++ return missingTags; ++ } ++ ++ public static void fetchCustomTagTypes(ITagCollectionSupplier tagCollectionSupplier) { ++ net.minecraftforge.common.ForgeTagHandler.getCustomTagTypeNames().forEach(tagRegistry -> field_242190_a.get(tagRegistry).func_242188_a(tagCollectionSupplier)); ++ } + } diff --git a/src/generated_test/resources/.cache/cache b/src/generated_test/resources/.cache/cache index 556aa2d5c..6eab62cfa 100644 --- a/src/generated_test/resources/.cache/cache +++ b/src/generated_test/resources/.cache/cache @@ -67,6 +67,8 @@ a012d6d92bab1c91913bd0f2dc0492a0fce916f2 assets/minecraft/blockstates/wall_torch d2c8e860521c5e738ed0798337f5728d610825ff assets/piston_event_test/models/block/shiftonmove.json f51f026a75e27a0a53a76771d553c0e3385a2966 assets/piston_event_test/models/item/shiftonmove.json cf16f861eaf5815238c2278eb48bde0688cb73b7 assets/scaffolding_test/blockstates/scaffolding_method_test.json +42d3ce7d941eb8b80c3d541b937d0f93c9cf7c3c data/custom_tag_types_test/tags/biomes/oceans.json +862196702dcd5538c5ceb4ac6202e57eb5c8a97f data/custom_tag_types_test/tags/custom_tag_types_test/custom_types/tests.json 4fbaf6f4a3ea05cc071076e27f44ac81f9cc50e3 data/data_gen_test/advancements/conditional.json ed4cbf1a3a2f5d8969f6346fdc9acdbe81d0c919 data/data_gen_test/recipes/conditional.json 40208299608468b044f64317995f9182ec219d90 data/data_gen_test/tags/blocks/test.json @@ -74,3 +76,6 @@ d65c425e740dc833f29d16606a1171825876be0d data/data_gen_test/tags/blocks/thing/on 1f70ed4ddc878bada5e43d3c44a34157806beda8 data/data_gen_test/tags/blocks/thing/three.json 2c648bca262caaa826ddbbde796d35bb2ee5f03a data/data_gen_test/tags/blocks/thing/two.json b0565ce75fc7880be7517e86a218d1ed9d9e346f data/data_gen_test/tags/blocks/things.json +7572ecfdcedc05dfcabf8a4c69c75e007023256f data/forge/loot_modifiers/global_loot_modifiers.json +f65e40b06ce7ad18e053ec88058b951f98ae5f40 data/global_loot_test/loot_modifiers/smelting.json +f991cfd612acf5ce98a43f4771ee04727219ae53 data/global_loot_test/loot_modifiers/wheat_harvest.json diff --git a/src/generated_test/resources/data/custom_tag_types_test/tags/biomes/oceans.json b/src/generated_test/resources/data/custom_tag_types_test/tags/biomes/oceans.json new file mode 100644 index 000000000..bb4783110 --- /dev/null +++ b/src/generated_test/resources/data/custom_tag_types_test/tags/biomes/oceans.json @@ -0,0 +1,15 @@ +{ + "replace": false, + "values": [ + "minecraft:ocean", + "minecraft:frozen_ocean", + "minecraft:deep_ocean", + "minecraft:warm_ocean", + "minecraft:lukewarm_ocean", + "minecraft:cold_ocean", + "minecraft:deep_warm_ocean", + "minecraft:deep_lukewarm_ocean", + "minecraft:deep_cold_ocean", + "minecraft:deep_frozen_ocean" + ] +} \ No newline at end of file diff --git a/src/generated_test/resources/data/custom_tag_types_test/tags/custom_tag_types_test/custom_types/tests.json b/src/generated_test/resources/data/custom_tag_types_test/tags/custom_tag_types_test/custom_types/tests.json new file mode 100644 index 000000000..f9040df20 --- /dev/null +++ b/src/generated_test/resources/data/custom_tag_types_test/tags/custom_tag_types_test/custom_types/tests.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "custom_tag_types_test:custom" + ] +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java b/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java index 47ff26671..b81c85d08 100644 --- a/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java +++ b/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java @@ -110,7 +110,7 @@ public class ForgeInternalHandler } @SubscribeEvent - public synchronized void tagsUpdated(TagsUpdatedEvent event) + public synchronized void tagsUpdated(TagsUpdatedEvent.VanillaTagTypes event) { ForgeHooks.updateBurns(); } diff --git a/src/main/java/net/minecraftforge/common/ForgeTagHandler.java b/src/main/java/net/minecraftforge/common/ForgeTagHandler.java new file mode 100644 index 000000000..5edc214b8 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/ForgeTagHandler.java @@ -0,0 +1,343 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Item; +import net.minecraft.resources.IResourceManager; +import net.minecraft.tags.ITag; +import net.minecraft.tags.ITag.INamedTag; +import net.minecraft.tags.ITagCollection; +import net.minecraft.tags.ITagCollectionSupplier; +import net.minecraft.tags.TagCollectionReader; +import net.minecraft.tags.TagRegistry; +import net.minecraft.tags.TagRegistryManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.Tags.IOptionalNamedTag; +import net.minecraftforge.fml.network.FMLPlayMessages.SyncCustomTagTypes; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.RegistryManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ForgeTagHandler +{ + private static final Logger LOGGER = LogManager.getLogger(); + private static Map> customTagTypes = Collections.emptyMap(); + private static Set customTagTypeNames = Collections.emptySet(); + private static boolean tagTypesSet = false; + + @Nullable + private static > TagRegistry getTagRegistry(IForgeRegistry registry) + { + return (TagRegistry) TagRegistryManager.get(registry.getRegistryName()); + } + + private static void validateRegistrySupportsTags(IForgeRegistry registry) + { + //Note: We also check against getTagRegistry in case someone decides to use the helpers for tag creation for types supported by vanilla + if (getTagRegistry(registry) == null && (!(registry instanceof ForgeRegistry) || ((ForgeRegistry) registry).getTagFolder() == null)) + { + throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types."); + } + } + + /** + * Helper method that creates a named tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types + * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically + * initializing and referencing the tag. + * @param registry Registry the tag is for + * @param name Name of the tag + * @param Type of the registry + * @return A named tag + */ + public static > ITag.INamedTag makeWrapperTag(IForgeRegistry registry, ResourceLocation name) + { + validateRegistrySupportsTags(registry); + if (tagTypesSet) + { + TagRegistry tagRegistry = getTagRegistry(registry); + if (tagRegistry == null) throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types."); + return tagRegistry.func_232937_a_(name.toString()); + } + return TagRegistry.createDelayedTag(registry.getRegistryName(), name); + } + + /** + * Helper method that creates an optional tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types + * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically + * initializing and referencing the tag. + * @param registry Registry the tag is for + * @param name Name of the tag + * @param Type of the registry + * @return An optional tag + */ + public static > IOptionalNamedTag createOptionalTag(IForgeRegistry registry, ResourceLocation name) + { + return createOptionalTag(registry, name, null); + } + + /** + * Helper method that creates an optional tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types + * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically + * initializing and referencing the tag. + * @param registry Registry the tag is for + * @param name Name of the tag + * @param defaults Default values for the optional tag + * @param Type of the registry + * @return An optional tag + */ + public static > IOptionalNamedTag createOptionalTag(IForgeRegistry registry, ResourceLocation name, @Nullable Supplier> defaults) + { + validateRegistrySupportsTags(registry); + if (tagTypesSet) + { + TagRegistry tagRegistry = getTagRegistry(registry); + if (tagRegistry == null) throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types."); + return tagRegistry.createOptional(name, defaults); + } + return TagRegistry.createDelayedOptional(registry.getRegistryName(), name, defaults); + } + + /** + * Helper method for creating named tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates + * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag. + * @param registryName Name of the registry the tag is for + * @param name Name of the tag + * @param Type of the registry + * @return A named tag + * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing + * and we can validate if the custom registry really does support custom tags. + */ + public static > ITag.INamedTag makeWrapperTag(ResourceLocation registryName, ResourceLocation name) + { + if (tagTypesSet) + { + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(registryName); + if (registry == null) throw new IllegalArgumentException("Could not find registry named: " + registryName); + return makeWrapperTag(registry, name); + } + return TagRegistry.createDelayedTag(registryName, name); + } + + /** + * Helper method for creating optional tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates + * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag. + * @param registryName Name of the registry the tag is for + * @param name Name of the tag + * @param Type of the registry + * @return An optional tag + * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing + * and we can validate if the custom registry really does support custom tags. + */ + public static > IOptionalNamedTag createOptionalTag(ResourceLocation registryName, ResourceLocation name) + { + return createOptionalTag(registryName, name, null); + } + + /** + * Helper method for creating optional tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates + * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag. + * @param registryName Name of the registry the tag is for + * @param name Name of the tag + * @param defaults Default values for the optional tag + * @param Type of the registry + * @return An optional tag + * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing + * and we can validate if the custom registry really does support custom tags. + */ + public static > IOptionalNamedTag createOptionalTag(ResourceLocation registryName, ResourceLocation name, @Nullable Supplier> defaults) + { + if (tagTypesSet) + { + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(registryName); + if (registry == null) throw new IllegalArgumentException("Could not find registry named: " + registryName); + return createOptionalTag(registry, name, defaults); + } + return TagRegistry.createDelayedOptional(registryName, name, defaults); + } + + /** + * Gets the all the registry names of registries that support custom tag types. + */ + public static Set getCustomTagTypeNames() + { + return customTagTypeNames; + } + + /** + * Gets a map of registry name to tag collection for all custom tag types. + * + * @apiNote Prefer interacting with this via the current {@link ITagCollectionSupplier} and using one of the forge extension getCustomTypeCollection methods + */ + public static Map> getCustomTagTypes() + { + return customTagTypes; + } + + /** + * Sets the set containing the resource locations representing the registry name of each forge registry that supports custom tag types. + * + * @apiNote Internal: Calling this manually WILL cause a crash to occur as it can only be called once, and is done so by + * forge after all registries have been initialized. + */ + public static void setCustomTagTypes(Set customTagTypes) + { + if (tagTypesSet) throw new RuntimeException("Custom tag types have already been set, this method should only be called by forge, and after registries are initialized"); + tagTypesSet = true; + customTagTypeNames = ImmutableSet.copyOf(customTagTypes); + TagRegistry.performDelayedAdd(); + } + + /** + * Creates a map for custom tag type to tag reader + * + * @apiNote Internal: For use by NetworkTagManager + */ + public static Map> createCustomTagTypeReaders() + { + LOGGER.debug("Gathering custom tag collection reader from types."); + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (ResourceLocation registryName : customTagTypeNames) + { + ForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(registryName); + if (registry != null && registry.getTagFolder() != null) + { + builder.put(registryName, new TagCollectionReader<>(rl -> Optional.ofNullable(registry.getValue(rl)), "tags/" + registry.getTagFolder(), registryName.getPath())); + } + } + return builder.build(); + } + + /** + * Resets the cached collections for the various custom tag types. + * + * @apiNote Internal + */ + public static void resetCachedTagCollections() + { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (ResourceLocation registryName : customTagTypeNames) + { + TagRegistry tagRegistry = TagRegistryManager.get(registryName); + if (tagRegistry != null) + { + builder.put(registryName, ITagCollection.func_242202_a(tagRegistry.func_241288_c_().stream().distinct().collect(Collectors.toMap(INamedTag::func_230234_a_, namedTag -> namedTag)))); + } + } + customTagTypes = builder.build(); + } + + /** + * Used to ensure that all custom tag types have a defaulted collection when vanilla is initializing a defaulted TagCollectionManager + * + * @apiNote Internal: For use by TagCollectionManager + */ + public static ITagCollectionSupplier populateTagCollectionManager(ITagCollection blockTags, ITagCollection itemTags, ITagCollection fluidTags, ITagCollection> entityTypeTags) + { + //Default the tag collections + resetCachedTagCollections(); + if (!customTagTypes.isEmpty()) + { + LOGGER.debug("Populated the TagCollectionManager with {} extra types", customTagTypes.size()); + } + return ITagCollectionSupplier.func_242209_a(blockTags, itemTags, fluidTags, entityTypeTags); + } + + /** + * Updates the custom tag types' tags from reloading via NetworkTagManager + * + * @apiNote Internal: For use by NetworkTagManager + */ + public static void updateCustomTagTypes(List tagCollectionReaders) + { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (TagCollectionReaderInfo info : tagCollectionReaders) + { + builder.put(info.tagType, info.reader.func_242226_a(info.tagBuilders)); + } + customTagTypes = builder.build(); + } + + /** + * Updates the custom tag types' tags from packet + * + * @apiNote Internal + */ + public static void updateCustomTagTypes(SyncCustomTagTypes packet) + { + customTagTypes = packet.getCustomTagTypes(); + } + + /** + * Gets the completable future containing the reload results for all custom tag types. + * + * @apiNote Internal: For use by NetworkTagManager + */ + public static CompletableFuture> getCustomTagTypeReloadResults(IResourceManager resourceManager, Executor backgroundExecutor, Map> readers) + { + CompletableFuture> customResults = CompletableFuture.completedFuture(new ArrayList<>()); + for (Map.Entry> entry : readers.entrySet()) + { + customResults = customResults.thenCombine(entry.getValue().func_242224_a(resourceManager, backgroundExecutor), (results, result) -> { + results.add(new TagCollectionReaderInfo(entry.getKey(), entry.getValue(), result)); + return results; + }); + } + return customResults; + } + + /** + * Helper storage class for keeping track of various data for all custom tag types in the NetworkTagReader to make the code easier to read. + * + * @apiNote Internal: For use by NetworkTagManager + */ + public static class TagCollectionReaderInfo + { + + private final ResourceLocation tagType; + private final TagCollectionReader reader; + private final Map tagBuilders; + + private TagCollectionReaderInfo(ResourceLocation tagType, TagCollectionReader reader, Map tagBuilders) + { + this.tagType = tagType; + this.reader = reader; + this.tagBuilders = tagBuilders; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java b/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java new file mode 100644 index 000000000..ead9df52b --- /dev/null +++ b/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java @@ -0,0 +1,73 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.data; + +import com.google.common.collect.ImmutableMap; +import com.mojang.serialization.Lifecycle; +import java.nio.file.Path; +import java.util.Map; +import javax.annotation.Nullable; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.TagsProvider; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.GameData; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; + +public abstract class ForgeRegistryTagsProvider> extends TagsProvider +{ + //Special handling for vanilla tag types in case someone decides to use the ForgeRegistryTagsProvider instead of one of the vanilla subtypes + private static final Map, String> vanillaTypes = ImmutableMap., String>builder() + .put(ForgeRegistries.BLOCKS, "blocks") + .put(ForgeRegistries.ENTITIES, "entity_types") + .put(ForgeRegistries.FLUIDS, "fluids") + .put(ForgeRegistries.ITEMS, "items") + .build(); + + private static > Registry wrapRegistry(IForgeRegistry registryIn) + { + if (!(registryIn instanceof ForgeRegistry)) + throw new IllegalArgumentException("Forge registry " + registryIn.getRegistryName() + " is not an instance of a ForgeRegistry"); + ForgeRegistry forgeRegistry = (ForgeRegistry) registryIn; + if (forgeRegistry.getTagFolder() == null && !vanillaTypes.containsKey(registryIn)) + throw new IllegalArgumentException("Forge registry " + registryIn.getRegistryName() + " does not have support for tags"); + if (forgeRegistry.getDefaultKey() == null) + return GameData.getWrapper(forgeRegistry.getRegistryKey(), Lifecycle.experimental()); + return GameData.getWrapper(forgeRegistry.getRegistryKey(), Lifecycle.experimental(), "default"); + } + + private final String folder; + + public ForgeRegistryTagsProvider(DataGenerator generatorIn, IForgeRegistry registryIn, String modId, @Nullable ExistingFileHelper existingFileHelper) + { + super(generatorIn, wrapRegistry(registryIn), modId, existingFileHelper); + String tagFolder = ((ForgeRegistry) registryIn).getTagFolder(); + folder = tagFolder == null ? vanillaTypes.get(registryIn) : tagFolder; + } + + @Override + protected Path makePath(ResourceLocation id) + { + return generator.getOutputFolder().resolve("data/" + id.getNamespace() + "/tags/" + folder + "/" + id.getPath() + ".json"); + } +} diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeTagBuilder.java b/src/main/java/net/minecraftforge/common/extensions/IForgeTagBuilder.java index eca6f72ff..2fa9295ce 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeTagBuilder.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeTagBuilder.java @@ -21,6 +21,7 @@ package net.minecraftforge.common.extensions; import net.minecraft.data.TagsProvider; import net.minecraft.tags.ITag; +import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; //TODO, Tag removal support. @@ -40,6 +41,14 @@ public interface IForgeTagBuilder return builder; } + default TagsProvider.Builder add(RegistryKey... keys) { + TagsProvider.Builder builder = getBuilder(); + for (RegistryKey key : keys) { + builder.getInternalBuilder().func_232961_a_(key.func_240901_a_(), getBuilder().getModID()); + } + return builder; + } + default TagsProvider.Builder replace() { return replace(true); } diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeTagCollectionSupplier.java b/src/main/java/net/minecraftforge/common/extensions/IForgeTagCollectionSupplier.java new file mode 100644 index 000000000..65818ce7b --- /dev/null +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeTagCollectionSupplier.java @@ -0,0 +1,61 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.extensions; + +import java.util.Map; +import net.minecraft.tags.ITagCollection; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.ForgeTagHandler; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; + +public interface IForgeTagCollectionSupplier +{ + /** + * Gets the tag map of registry names to tag collections for the various custom tag types. + * + * @apiNote Prefer using one of the getCustomTypeCollection methods + */ + default Map> getCustomTagTypes() + { + return ForgeTagHandler.getCustomTagTypes(); + } + + /** + * Gets the {@link ITagCollection} for a forge registry with the given name, or throws an exception if the registry doesn't support custom tag types. + * @param regName Name of the forge registry + * @return The tag collection + */ + default ITagCollection getCustomTypeCollection(ResourceLocation regName) + { + if (!ForgeTagHandler.getCustomTagTypeNames().contains(regName)) throw new IllegalArgumentException("Registry " + regName + ", does not support custom tag types"); + return getCustomTagTypes().get(regName); + } + + /** + * Gets the {@link ITagCollection} for a forge registry, or throws an exception if the registry doesn't support custom tag types. + * @param reg Forge registry + * @return The tag collection + */ + default > ITagCollection getCustomTypeCollection(IForgeRegistry reg) + { + return (ITagCollection) getCustomTypeCollection(reg.getRegistryName()); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/event/TagsUpdatedEvent.java b/src/main/java/net/minecraftforge/event/TagsUpdatedEvent.java index c90e4473c..cd918c556 100644 --- a/src/main/java/net/minecraftforge/event/TagsUpdatedEvent.java +++ b/src/main/java/net/minecraftforge/event/TagsUpdatedEvent.java @@ -45,4 +45,26 @@ public class TagsUpdatedEvent extends Event { return manager; } + + /** + * Fired after the Vanilla Tag types have been processed + */ + public static class VanillaTagTypes extends TagsUpdatedEvent + { + public VanillaTagTypes(ITagCollectionSupplier manager) + { + super(manager); + } + } + + /** + * Fired after any custom tag types have been processed + */ + public static class CustomTagTypes extends TagsUpdatedEvent + { + public CustomTagTypes(ITagCollectionSupplier manager) + { + super(manager); + } + } } diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index b2356aae9..7012c525a 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -198,6 +198,8 @@ public class ModLoader CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData()); statusConsumer.ifPresent(c->c.accept("Populating registries")); dispatchAndHandleError(ModLoadingStage.LOAD_REGISTRIES, syncExecutor, parallelExecutor, periodicTask); + statusConsumer.ifPresent(c->c.accept("Adding custom tag types")); + GameData.setCustomTagTypesFromRegistries(); statusConsumer.ifPresent(c->c.accept("Early mod loading complete")); } diff --git a/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java b/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java index d692aa500..dd501e8eb 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java @@ -19,7 +19,14 @@ package net.minecraftforge.fml.network; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import io.netty.buffer.Unpooled; +import java.util.List; +import java.util.Map; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.IHasContainer; import net.minecraft.client.gui.ScreenManager; @@ -27,23 +34,36 @@ import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.fluid.Fluid; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; +import net.minecraft.item.Item; import net.minecraft.network.PacketBuffer; +import net.minecraft.tags.ITag; +import net.minecraft.tags.ITagCollection; +import net.minecraft.tags.ITagCollectionSupplier; +import net.minecraft.tags.TagRegistryManager; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; -import net.minecraft.world.biome.FuzzedBiomeMagnifier; +import net.minecraftforge.common.ForgeTagHandler; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TagsUpdatedEvent; import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraft.util.registry.Registry; import net.minecraft.util.text.ITextComponent; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; -import net.minecraftforge.registries.ForgeRegistries; import java.util.Optional; import java.util.UUID; import java.util.function.Supplier; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.RegistryManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class FMLPlayMessages { @@ -308,4 +328,144 @@ public class FMLPlayMessages return additionalData; } } + + public static class SyncCustomTagTypes + { + private static final Logger LOGGER = LogManager.getLogger(); + private final Map> customTagTypeCollections; + + SyncCustomTagTypes(Map> customTagTypeCollections) + { + this.customTagTypeCollections = customTagTypeCollections; + } + + public Map> getCustomTagTypes() + { + return customTagTypeCollections; + } + + public static void encode(SyncCustomTagTypes msg, PacketBuffer buf) + { + buf.writeVarInt(msg.customTagTypeCollections.size()); + msg.customTagTypeCollections.forEach((registryName, modded) -> forgeTagCollectionWrite(buf, registryName, modded.func_241833_a())); + } + + private static void forgeTagCollectionWrite(PacketBuffer buf, ResourceLocation registryName, Map> tags) + { + buf.writeResourceLocation(registryName); + buf.writeVarInt(tags.size()); + tags.forEach((name, tag) -> { + buf.writeResourceLocation(name); + List elements = tag.func_230236_b_(); + buf.writeVarInt(elements.size()); + for (T element : elements) + { + buf.writeResourceLocation(((IForgeRegistryEntry) element).getRegistryName()); + } + }); + } + + public static SyncCustomTagTypes decode(PacketBuffer buf) + { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = buf.readVarInt(); + for (int i = 0; i < size; i++) + { + ResourceLocation regName = buf.readResourceLocation(); + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(regName); + if (registry != null) + { + builder.put(regName, readTagCollection(buf, registry)); + } + } + return new SyncCustomTagTypes(builder.build()); + } + + private static > ITagCollection readTagCollection(PacketBuffer buf, IForgeRegistry registry) + { + Map> tags = Maps.newHashMap(); + int totalTags = buf.readVarInt(); + for (int i = 0; i < totalTags; i++) + { + ImmutableSet.Builder elementBuilder = ImmutableSet.builder(); + ResourceLocation name = buf.readResourceLocation(); + int totalElements = buf.readVarInt(); + for (int j = 0; j < totalElements; j++) + { + T element = registry.getValue(buf.readResourceLocation()); + if (element != null) + { + elementBuilder.add(element); + } + } + tags.put(name, ITag.func_232946_a_(elementBuilder.build())); + } + return ITagCollection.func_242202_a(tags); + } + + public static void handle(SyncCustomTagTypes msg, Supplier ctx) + { + ctx.get().enqueueWork(() -> { + if (Minecraft.getInstance().world != null) + { + ITagCollectionSupplier tagCollectionSupplier = Minecraft.getInstance().world.getTags(); + //Validate that all the tags exist using the tag type collections from the packet + // We mimic vanilla in that we validate before updating the actual stored tags so that it can gracefully fallback + // to the last working set of tags + //Note: We gracefully ignore any tag types the server may have that we don't as they won't be in our tag registry + // so they won't be validated + Multimap missingTags = TagRegistryManager.func_242198_b(new ITagCollectionSupplier() + { + @Override + public ITagCollection func_241835_a() + { + return tagCollectionSupplier.func_241835_a(); + } + + @Override + public ITagCollection func_241836_b() + { + return tagCollectionSupplier.func_241836_b(); + } + + @Override + public ITagCollection func_241837_c() + { + return tagCollectionSupplier.func_241837_c(); + } + + @Override + public ITagCollection> func_241838_d() + { + return tagCollectionSupplier.func_241838_d(); + } + + @Override + public Map> getCustomTagTypes() + { + //Override and use the tags from the packet to test for validation before we actually set them + return msg.customTagTypeCollections; + } + }); + if (missingTags.isEmpty()) + { + //If we have no missing tags, update the custom tag types + ForgeTagHandler.updateCustomTagTypes(msg); + if (!ctx.get().getNetworkManager().isLocalChannel()) + { + //And if everything hasn't already been set due to being in single player + // Fetch and update the custom tag types. We skip vanilla tag types as they have already been fetched + // And fire an event that the custom tag types have been updated + TagRegistryManager.fetchCustomTagTypes(tagCollectionSupplier); + MinecraftForge.EVENT_BUS.post(new TagsUpdatedEvent.CustomTagTypes(tagCollectionSupplier)); + } + } else { + LOGGER.warn("Incomplete server tags, disconnecting. Missing: {}", missingTags); + ctx.get().getNetworkManager().closeChannel(new TranslationTextComponent("multiplayer.disconnect.missing_tags")); + } + } + }); + ctx.get().setPacketHandled(true); + } + } } diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java index 8fb356d80..a3adfb406 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java @@ -19,6 +19,7 @@ package net.minecraftforge.fml.network; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -26,6 +27,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; +import net.minecraft.tags.ITagCollection; +import net.minecraft.tags.ITagCollectionSupplier; import net.minecraft.util.text.StringTextComponent; import net.minecraftforge.fml.common.thread.EffectiveSide; import org.apache.logging.log4j.LogManager; @@ -208,4 +211,31 @@ public class NetworkHooks player.openContainer.addListener(player); MinecraftForge.EVENT_BUS.post(new PlayerContainerEvent.Open(player, c)); } + + /** + * Syncs the custom tag types attached to a {@link ITagCollectionSupplier} to all connected players. + * @param tagCollectionSupplier The tag collection supplier containing the custom tags + */ + public static void syncCustomTagTypes(ITagCollectionSupplier tagCollectionSupplier) + { + Map> customTagTypes = tagCollectionSupplier.getCustomTagTypes(); + if (!customTagTypes.isEmpty()) + { + FMLNetworkConstants.playChannel.send(PacketDistributor.ALL.noArg(), new FMLPlayMessages.SyncCustomTagTypes(customTagTypes)); + } + } + + /** + * Syncs the custom tag types attached to a {@link ITagCollectionSupplier} to the given player. + * @param player The player to sync the custom tags to. + * @param tagCollectionSupplier The tag collection supplier containing the custom tags + */ + public static void syncCustomTagTypes(ServerPlayerEntity player, ITagCollectionSupplier tagCollectionSupplier) + { + Map> customTagTypes = tagCollectionSupplier.getCustomTagTypes(); + if (!customTagTypes.isEmpty()) + { + FMLNetworkConstants.playChannel.sendTo(new FMLPlayMessages.SyncCustomTagTypes(customTagTypes), player.connection.getNetworkManager(), NetworkDirection.PLAY_TO_CLIENT); + } + } } diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java b/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java index 22ded8224..473416f8d 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java @@ -105,6 +105,12 @@ class NetworkInitialization { // .consumer(FMLPlayMessages.DimensionInfoMessage::handle) // .add(); + playChannel.messageBuilder(FMLPlayMessages.SyncCustomTagTypes.class, 3). + decoder(FMLPlayMessages.SyncCustomTagTypes::decode). + encoder(FMLPlayMessages.SyncCustomTagTypes::encode). + consumer(FMLPlayMessages.SyncCustomTagTypes::handle). + add(); + return playChannel; } diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 455c0547b..a382ef484 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -91,6 +91,8 @@ public class ForgeRegistry> implements IForgeRe private final int max; private final boolean allowOverrides; private final boolean isModifiable; + @Nullable + private final String tagFolder; private V defaultValue = null; boolean isFrozen = false; @@ -120,6 +122,7 @@ public class ForgeRegistry> implements IForgeRe this.isDelegated = ForgeRegistryEntry.class.isAssignableFrom(superType); //TODO: Make this IDelegatedRegistryEntry? this.allowOverrides = builder.getAllowOverrides(); this.isModifiable = builder.getAllowModifications(); + this.tagFolder = builder.getTagFolder(); if (this.create != null) this.create.onCreate(this, stage); } @@ -175,6 +178,12 @@ public class ForgeRegistry> implements IForgeRe return superType; } + @Nullable + public String getTagFolder() + { + return tagFolder; + } + @Override public void registerAll(@SuppressWarnings("unchecked") V... values) { diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 587738169..0706394b3 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -22,6 +22,8 @@ package net.minecraftforge.registries; import com.google.common.collect.*; import com.mojang.serialization.Lifecycle; +import java.util.HashSet; +import java.util.Set; import net.minecraft.block.AirBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -47,6 +49,7 @@ import net.minecraft.potion.Effect; import net.minecraft.potion.Potion; import net.minecraft.state.StateContainer; import net.minecraft.stats.StatType; +import net.minecraft.tags.TagRegistryManager; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.ObjectIntIdentityMap; import net.minecraft.util.RegistryKey; @@ -68,6 +71,7 @@ import net.minecraft.world.gen.foliageplacer.FoliagePlacerType; import net.minecraft.world.gen.placement.Placement; import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; import net.minecraft.world.gen.treedecorator.TreeDecoratorType; +import net.minecraftforge.common.ForgeTagHandler; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.loot.GlobalLootModifierSerializer; import net.minecraftforge.event.RegistryEvent; @@ -175,7 +179,7 @@ public class GameData makeRegistry(TREE_DECORATOR_TYPES, c(TreeDecoratorType.class)).disableSaving().disableSync().create(); // Dynamic Worldgen - makeRegistry(BIOMES, Biome.class).create(); + makeRegistry(BIOMES, Biome.class).tagFolder("biomes").create(); // Custom forge registries makeRegistry(DATA_SERIALIZERS, DataSerializerEntry.class, 256 /*vanilla space*/, MAX_VARINT).disableSaving().disableOverrides().addCallback(SerializerCallbacks.INSTANCE).create(); @@ -190,11 +194,11 @@ public class GameData } private static > RegistryBuilder makeRegistry(RegistryKey> key, Class type, int min, int max) { - return new RegistryBuilder().setName(key.func_240901_a_()).setType(type).setIDRange(min, max).addCallback(new NamespacedWrapper.Factory()); + return new RegistryBuilder().setName(key.func_240901_a_()).setType(type).setIDRange(min, max).hasWrapper(); } private static > RegistryBuilder makeRegistry(RegistryKey> key, Class type, String _default) { - return new RegistryBuilder().setName(key.func_240901_a_()).setType(type).setMaxID(MAX_VARINT).addCallback(new NamespacedDefaultedWrapper.Factory()).setDefaultKey(new ResourceLocation(_default)); + return new RegistryBuilder().setName(key.func_240901_a_()).setType(type).setMaxID(MAX_VARINT).hasWrapper().setDefaultKey(new ResourceLocation(_default)); } public static > SimpleRegistry getWrapper(RegistryKey> key, Lifecycle lifecycle) @@ -360,6 +364,22 @@ public class GameData }; } + public static void setCustomTagTypesFromRegistries() + { + Set customTagTypes = new HashSet<>(); + for (Map.Entry>> entry : RegistryManager.ACTIVE.registries.entrySet()) + { + ResourceLocation registryName = entry.getKey(); + if (entry.getValue().getTagFolder() != null && TagRegistryManager.get(registryName) == null) + { + LOGGER.debug(REGISTRIES, "Registering custom tag type for: {}", registryName); + customTagTypes.add(registryName); + TagRegistryManager.func_242196_a(registryName, tagCollectionSupplier -> tagCollectionSupplier.getCustomTypeCollection(registryName)); + } + } + ForgeTagHandler.setCustomTagTypes(customTagTypes); + } + //Lets us clear the map so we can rebuild it. private static class ClearableObjectIntIdentityMap extends ObjectIntIdentityMap { diff --git a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java index 42b006133..a8dc4fea2 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java @@ -48,6 +48,9 @@ public class RegistryBuilder> private boolean sync = true; private boolean allowOverrides = true; private boolean allowModifications = false; + private boolean hasWrapper = false; + @Nullable + private String tagFolder; private DummyFactory dummyFactory; private MissingFactory missingFactory; private Set legacyNames = new HashSet<>(); @@ -203,6 +206,21 @@ public class RegistryBuilder> return this; } + RegistryBuilder hasWrapper() + { + this.hasWrapper = true; + return this; + } + + public RegistryBuilder tagFolder(String tagFolder) + { + if (tagFolder == null || !tagFolder.matches("[a-z_/]+")) throw new IllegalArgumentException("Non [a-z_/] character in tag folder " + tagFolder); + this.tagFolder = tagFolder; + //Also mark this registry as having a wrapper to a vanilla registry so that it can be used in data generators properly + hasWrapper(); + return this; + } + public RegistryBuilder legacyName(String name) { return legacyName(new ResourceLocation(name)); @@ -216,6 +234,13 @@ public class RegistryBuilder> public IForgeRegistry create() { + if (hasWrapper) + { + if (getDefault() == null) + addCallback(new NamespacedWrapper.Factory()); + else + addCallback(new NamespacedDefaultedWrapper.Factory()); + } return RegistryManager.ACTIVE.createRegistry(registryName, this); } @@ -325,6 +350,12 @@ public class RegistryBuilder> return allowModifications; } + @Nullable + public String getTagFolder() + { + return tagFolder; + } + @Nullable public DummyFactory getDummyFactory() { diff --git a/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java b/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java index 802bb8c69..99441f663 100644 --- a/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java +++ b/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java @@ -66,7 +66,7 @@ import java.util.List; @Mod(GlobalLootModifiersTest.MODID) public class GlobalLootModifiersTest { public static final String MODID = "global_loot_test"; - public static final boolean ENABLE = false; + public static final boolean ENABLE = true; public GlobalLootModifiersTest() { diff --git a/src/test/java/net/minecraftforge/debug/misc/CustomTagTypesTest.java b/src/test/java/net/minecraftforge/debug/misc/CustomTagTypesTest.java new file mode 100644 index 000000000..8793da312 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/misc/CustomTagTypesTest.java @@ -0,0 +1,114 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug.misc; + +import java.util.function.Supplier; +import javax.annotation.Nullable; +import net.minecraft.data.DataGenerator; +import net.minecraft.tags.ITag; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraftforge.common.ForgeTagHandler; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.common.data.ForgeRegistryTagsProvider; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.RegistryObject; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistryEntry; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.RegistryBuilder; + +@Mod(CustomTagTypesTest.MODID) +public class CustomTagTypesTest +{ + public static final String MODID = "custom_tag_types_test"; + private static final ResourceLocation customRegistryName = new ResourceLocation(MODID, "custom_type_registry"); + private static final DeferredRegister CUSTOMS = DeferredRegister.create(Custom.class, MODID); + private static final RegistryObject CUSTOM = CUSTOMS.register("custom", Custom::new); + private static final Supplier> CUSTOM_REG = CUSTOMS.makeRegistry(customRegistryName.getPath(), + () -> new RegistryBuilder().tagFolder(MODID + "/custom_types")); + private static final ITag.INamedTag OCEANS = ForgeTagHandler.makeWrapperTag(ForgeRegistries.BIOMES, new ResourceLocation(MODID, "oceans")); + private static final ITag.INamedTag TESTS = ForgeTagHandler.makeWrapperTag(customRegistryName, new ResourceLocation(MODID, "tests")); + + public CustomTagTypesTest() + { + IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); + CUSTOMS.register(modBus); + modBus.addListener(this::gatherData); + } + + private void gatherData(GatherDataEvent event) + { + if (event.includeServer()) + { + DataGenerator gen = event.getGenerator(); + ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); + gen.addProvider(new BiomeTags(gen, existingFileHelper)); + gen.addProvider(new CustomRegistryTags(gen, existingFileHelper)); + } + } + + public static class Custom extends ForgeRegistryEntry { + } + + public static class BiomeTags extends ForgeRegistryTagsProvider + { + public BiomeTags(DataGenerator gen, @Nullable ExistingFileHelper existingFileHelper) + { + super(gen, ForgeRegistries.BIOMES, MODID, existingFileHelper); + } + + @Override + protected void registerTags() + { + func_240522_a_(OCEANS).add(Biomes.OCEAN, Biomes.FROZEN_OCEAN, Biomes.DEEP_OCEAN, Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, + Biomes.COLD_OCEAN, Biomes.DEEP_WARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN, Biomes.DEEP_COLD_OCEAN, Biomes.DEEP_FROZEN_OCEAN); + } + + @Override + public String getName() { + return "Biome Tags"; + } + } + + public static class CustomRegistryTags extends ForgeRegistryTagsProvider + { + public CustomRegistryTags(DataGenerator gen, @Nullable ExistingFileHelper existingFileHelper) + { + super(gen, CUSTOM_REG.get(), MODID, existingFileHelper); + } + + @Override + protected void registerTags() + { + func_240522_a_(TESTS).func_240532_a_(CUSTOM.get()); + } + + @Override + public String getName() { + return "Custom Registry Tags"; + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index d0ad2eb55..6981959e1 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -82,3 +82,5 @@ license="LGPL v2.1" modId="finite_water_test" [[mods]] modId="scaffolding_test" +[[mods]] + modId="custom_tag_types_test"