From 2e42340dfa80cb95f78ec2774d3022cf86606310 Mon Sep 17 00:00:00 2001 From: cpw Date: Mon, 31 Aug 2020 21:00:44 -0400 Subject: [PATCH] Fix up all the things. Removed SidedProvider because it served no real purpose anymore. Signed-off-by: cpw --- .../net/minecraft/client/Minecraft.java.patch | 66 ++++++++-------- .../crash/CrashReportCategory.java.patch | 11 +++ .../net/minecraft/data/Main.java.patch | 2 +- .../item/crafting/Ingredient.java.patch | 2 +- .../event/AddReloadListenerEvent.java | 22 +++++- .../minecraftforge/fml/DatagenModLoader.java | 34 ++++++++ .../minecraftforge/fml/DeferredWorkQueue.java | 21 +++-- .../net/minecraftforge/fml/ForgeI18n.java | 19 +++++ .../net/minecraftforge/fml/ModLoader.java | 54 ++++++++----- .../minecraftforge/fml/ModLoadingContext.java | 1 + .../fml/ModLoadingException.java | 9 +++ .../minecraftforge/fml/ModLoadingStage.java | 10 ++- .../minecraftforge/fml/ModWorkManager.java | 4 +- .../net/minecraftforge/fml/SidedProvider.java | 78 ------------------- .../fml/client/ClientHooks.java | 9 --- .../fml/client/ClientModLoader.java | 59 ++++++++++++-- .../fml/event/lifecycle/GatherDataEvent.java | 13 +++- .../fml/packs/ResourcePackLoader.java | 10 +-- .../fml/server/ServerLifecycleHooks.java | 28 ------- .../fml/server/ServerModLoader.java | 34 ++++++-- 20 files changed, 282 insertions(+), 204 deletions(-) create mode 100644 src/main/java/net/minecraftforge/fml/DatagenModLoader.java delete mode 100644 src/main/java/net/minecraftforge/fml/SidedProvider.java diff --git a/patches/minecraft/net/minecraft/client/Minecraft.java.patch b/patches/minecraft/net/minecraft/client/Minecraft.java.patch index 0b8cc56c0..26f8a7cf3 100644 --- a/patches/minecraft/net/minecraft/client/Minecraft.java.patch +++ b/patches/minecraft/net/minecraft/client/Minecraft.java.patch @@ -16,7 +16,7 @@ this.field_71459_aj = p_i45547_1_.field_178741_d.field_178756_a; this.field_238175_ae_ = !p_i45547_1_.field_178741_d.field_239099_d_; this.field_238176_af_ = !p_i45547_1_.field_178741_d.field_239100_e_; -@@ -416,14 +416,15 @@ +@@ -416,13 +416,13 @@ this.field_195558_d.func_216526_a(this.field_71474_y.field_74350_i); this.field_71417_B = new MouseHelper(this); @@ -29,11 +29,9 @@ this.field_110451_am = new SimpleReloadableResourceManager(ResourcePackType.CLIENT_RESOURCES); + net.minecraftforge.fml.client.ClientModLoader.begin(this, this.field_110448_aq, this.field_110451_am, this.field_195554_ax); this.field_110448_aq.func_198983_a(); -+ DatapackCodec.field_234880_a_.addModPacks(net.minecraftforge.common.ForgeHooks.getModPacks()); this.field_71474_y.func_198017_a(this.field_110448_aq); this.field_135017_as = new LanguageManager(this.field_71474_y.field_74363_ab); - this.field_110451_am.func_219534_a(this.field_135017_as); -@@ -463,6 +464,7 @@ +@@ -463,6 +463,7 @@ this.func_193986_ar(); this.field_110451_am.func_219534_a(this.field_193995_ae); this.field_71452_i = new ParticleManager(this.field_71441_e, this.field_71446_o); @@ -41,7 +39,7 @@ this.field_110451_am.func_219534_a(this.field_71452_i); this.field_213272_aL = new PaintingSpriteUploader(this.field_71446_o); this.field_110451_am.func_219534_a(this.field_213272_aL); -@@ -470,7 +472,8 @@ +@@ -470,7 +471,8 @@ this.field_110451_am.func_219534_a(this.field_213273_aM); this.field_241557_ar_ = new GPUWarning(); this.field_110451_am.func_219534_a(this.field_241557_ar_); @@ -51,7 +49,7 @@ this.field_184132_p = new DebugRenderer(this); RenderSystem.setErrorCallback(this::func_195545_a); if (this.field_71474_y.field_74353_u && !this.field_195558_d.func_198113_j()) { -@@ -482,11 +485,6 @@ +@@ -482,11 +484,6 @@ this.field_195558_d.func_224798_d(this.field_71474_y.field_225307_E); this.field_195558_d.func_227801_c_(); this.func_213226_a(); @@ -63,7 +61,7 @@ ResourceLoadProgressGui.func_212970_a(this); List list = this.field_110448_aq.func_232623_f_(); -@@ -495,7 +493,14 @@ +@@ -495,7 +492,14 @@ if (SharedConstants.field_206244_b) { this.func_213256_aB(); } @@ -79,7 +77,7 @@ }); }, false)); } -@@ -534,7 +539,7 @@ +@@ -534,7 +538,7 @@ } private void func_229988_a_(Throwable p_229988_1_) { @@ -88,7 +86,7 @@ ITextComponent itextcomponent; if (p_229988_1_ instanceof SimpleReloadableResourceManager.FailedPackException) { itextcomponent = new StringTextComponent(((SimpleReloadableResourceManager.FailedPackException)p_229988_1_).func_241203_a().func_195762_a()); -@@ -618,7 +623,7 @@ +@@ -618,7 +622,7 @@ return Stream.of(Registry.field_212630_s.func_177774_c(p_213251_0_.func_77973_b())); }); SearchTreeReloadable searchtreereloadable = new SearchTreeReloadable<>((p_213235_0_) -> { @@ -97,7 +95,7 @@ }); NonNullList nonnulllist = NonNullList.func_191196_a(); -@@ -688,13 +693,13 @@ +@@ -688,13 +692,13 @@ Bootstrap.func_179870_a(p_71377_0_.func_71502_e()); if (p_71377_0_.func_71497_f() != null) { Bootstrap.func_179870_a("#@!@# Game crashed! Crash report saved to: #@!@# " + p_71377_0_.func_71497_f()); @@ -114,7 +112,7 @@ } } -@@ -703,6 +708,7 @@ +@@ -703,6 +707,7 @@ return this.field_71474_y.field_211842_aO; } @@ -122,7 +120,7 @@ public CompletableFuture func_213237_g() { if (this.field_213276_aV != null) { return this.field_213276_aV; -@@ -791,10 +797,6 @@ +@@ -791,10 +796,6 @@ } public void func_147108_a(@Nullable Screen p_147108_1_) { @@ -133,7 +131,7 @@ if (p_147108_1_ == null && this.field_71441_e == null) { p_147108_1_ = new MainMenuScreen(); } else if (p_147108_1_ == null && this.field_71439_g.func_233643_dh_()) { -@@ -805,6 +807,14 @@ +@@ -805,6 +806,14 @@ } } @@ -148,7 +146,7 @@ if (p_147108_1_ instanceof MainMenuScreen || p_147108_1_ instanceof MultiplayerScreen) { this.field_71474_y.field_74330_P = false; this.field_71456_v.func_146158_b().func_146231_a(true); -@@ -936,11 +946,13 @@ +@@ -936,11 +945,13 @@ RenderSystem.enableCull(); this.field_71424_I.func_76319_b(); if (!this.field_71454_w) { @@ -162,7 +160,7 @@ } if (this.field_238174_aV_ != null) { -@@ -1039,7 +1051,7 @@ +@@ -1039,7 +1050,7 @@ Framebuffer framebuffer = this.func_147110_a(); framebuffer.func_216491_a(this.field_195558_d.func_198109_k(), this.field_195558_d.func_198091_l(), field_142025_a); @@ -171,7 +169,7 @@ this.field_71417_B.func_198021_g(); } -@@ -1227,11 +1239,21 @@ +@@ -1227,11 +1238,21 @@ if (p_147115_1_ && this.field_71476_x != null && this.field_71476_x.func_216346_c() == RayTraceResult.Type.BLOCK) { BlockRayTraceResult blockraytraceresult = (BlockRayTraceResult)this.field_71476_x; BlockPos blockpos = blockraytraceresult.func_216350_a(); @@ -195,7 +193,7 @@ } } -@@ -1250,6 +1272,8 @@ +@@ -1250,6 +1271,8 @@ } } else if (!this.field_71439_g.func_184838_M()) { @@ -204,7 +202,7 @@ switch(this.field_71476_x.func_216346_c()) { case ENTITY: this.field_71442_b.func_78764_a(this.field_71439_g, ((EntityRayTraceResult)this.field_71476_x).func_216348_a()); -@@ -1257,7 +1281,7 @@ +@@ -1257,7 +1280,7 @@ case BLOCK: BlockRayTraceResult blockraytraceresult = (BlockRayTraceResult)this.field_71476_x; BlockPos blockpos = blockraytraceresult.func_216350_a(); @@ -213,7 +211,7 @@ this.field_71442_b.func_180511_b(blockpos, blockraytraceresult.func_216354_b()); break; } -@@ -1267,8 +1291,10 @@ +@@ -1267,8 +1290,10 @@ } this.field_71439_g.func_184821_cY(); @@ -224,7 +222,7 @@ this.field_71439_g.func_184609_a(Hand.MAIN_HAND); } } -@@ -1283,6 +1309,11 @@ +@@ -1283,6 +1308,11 @@ } for(Hand hand : Hand.values()) { @@ -236,7 +234,7 @@ ItemStack itemstack = this.field_71439_g.func_184586_b(hand); if (this.field_71476_x != null) { switch(this.field_71476_x.func_216346_c()) { -@@ -1296,6 +1327,7 @@ +@@ -1296,6 +1326,7 @@ if (actionresulttype.func_226246_a_()) { if (actionresulttype.func_226247_b_()) { @@ -244,7 +242,7 @@ this.field_71439_g.func_184609_a(hand); } -@@ -1308,6 +1340,7 @@ +@@ -1308,6 +1339,7 @@ ActionResultType actionresulttype1 = this.field_71442_b.func_217292_a(this.field_71439_g, this.field_71441_e, hand, blockraytraceresult); if (actionresulttype1.func_226246_a_()) { if (actionresulttype1.func_226247_b_()) { @@ -252,7 +250,7 @@ this.field_71439_g.func_184609_a(hand); if (!itemstack.func_190926_b() && (itemstack.func_190916_E() != i || this.field_71442_b.func_78758_h())) { this.field_71460_t.field_78516_c.func_187460_a(hand); -@@ -1323,6 +1356,9 @@ +@@ -1323,6 +1355,9 @@ } } @@ -262,7 +260,7 @@ if (!itemstack.func_190926_b()) { ActionResultType actionresulttype2 = this.field_71442_b.func_187101_a(this.field_71439_g, this.field_71441_e, hand); if (actionresulttype2.func_226246_a_()) { -@@ -1349,6 +1385,8 @@ +@@ -1349,6 +1384,8 @@ --this.field_71467_ac; } @@ -271,7 +269,7 @@ this.field_71424_I.func_76320_a("gui"); if (!this.field_71445_n) { this.field_71456_v.func_73831_a(); -@@ -1465,6 +1503,8 @@ +@@ -1465,6 +1502,8 @@ this.field_71424_I.func_219895_b("keyboard"); this.field_195559_v.func_204870_b(); this.field_71424_I.func_76319_b(); @@ -280,7 +278,7 @@ } private void func_184117_aA() { -@@ -1696,7 +1736,12 @@ +@@ -1696,7 +1735,12 @@ networkmanager.func_150719_a(new ClientLoginNetHandler(networkmanager, this, (Screen)null, (p_229998_0_) -> { })); networkmanager.func_179290_a(new CHandshakePacket(socketaddress.toString(), 0, ProtocolType.LOGIN)); @@ -294,7 +292,7 @@ this.field_71453_ak = networkmanager; } else { this.func_241559_a_(p_238195_6_, p_238195_1_, flag, () -> { -@@ -1770,6 +1815,7 @@ +@@ -1770,6 +1814,7 @@ } public void func_71403_a(ClientWorld p_71403_1_) { @@ -302,7 +300,7 @@ WorkingScreen workingscreen = new WorkingScreen(); workingscreen.func_200210_a(new TranslationTextComponent("connect.joining")); this.func_213241_c(workingscreen); -@@ -1801,10 +1847,12 @@ +@@ -1801,10 +1846,12 @@ IntegratedServer integratedserver = this.field_71437_Z; this.field_71437_Z = null; this.field_71460_t.func_190564_k(); @@ -315,7 +313,7 @@ if (integratedserver != null) { this.field_71424_I.func_76320_a("waitForServer"); -@@ -1819,6 +1867,7 @@ +@@ -1819,6 +1866,7 @@ this.field_71456_v.func_181029_i(); this.field_71422_O = null; this.field_71455_al = false; @@ -323,7 +321,7 @@ this.field_213274_aO.func_216815_b(); } -@@ -1849,6 +1898,7 @@ +@@ -1849,6 +1897,7 @@ this.field_71452_i.func_78870_a(p_213257_1_); TileEntityRendererDispatcher.field_147556_a.func_147543_a(p_213257_1_); this.func_230150_b_(); @@ -331,7 +329,7 @@ } public boolean func_238216_r_() { -@@ -1894,112 +1944,9 @@ +@@ -1894,112 +1943,9 @@ private void func_147112_ai() { if (this.field_71476_x != null && this.field_71476_x.func_216346_c() != RayTraceResult.Type.MISS) { @@ -447,7 +445,7 @@ } } -@@ -2081,6 +2028,7 @@ +@@ -2081,6 +2027,7 @@ return field_71432_P; } @@ -455,7 +453,7 @@ public CompletableFuture func_213245_w() { return this.func_213169_a(this::func_213237_g).thenCompose((p_229993_0_) -> { return p_229993_0_; -@@ -2388,7 +2336,7 @@ +@@ -2388,7 +2335,7 @@ supplier = func_228022_c_(supplier); } @@ -464,7 +462,7 @@ } private static Supplier func_228021_b_(Supplier p_228021_0_) { -@@ -2407,6 +2355,14 @@ +@@ -2407,6 +2354,14 @@ this.field_175617_aL.func_229355_a_(p_228020_1_); } diff --git a/patches/minecraft/net/minecraft/crash/CrashReportCategory.java.patch b/patches/minecraft/net/minecraft/crash/CrashReportCategory.java.patch index 51237b55f..1f9144e5d 100644 --- a/patches/minecraft/net/minecraft/crash/CrashReportCategory.java.patch +++ b/patches/minecraft/net/minecraft/crash/CrashReportCategory.java.patch @@ -13,3 +13,14 @@ return this.field_85075_d.length; } } +@@ -158,6 +160,10 @@ + return this.field_85075_d; + } + ++ public void applyStackTrace(Throwable t) { ++ this.field_85075_d = t.getStackTrace(); ++ } ++ + public static void func_175750_a(CrashReportCategory p_175750_0_, BlockPos p_175750_1_, @Nullable BlockState p_175750_2_) { + if (p_175750_2_ != null) { + p_175750_0_.func_189529_a("Block", p_175750_2_::toString); diff --git a/patches/minecraft/net/minecraft/data/Main.java.patch b/patches/minecraft/net/minecraft/data/Main.java.patch index 24c6b3f1d..48b279199 100644 --- a/patches/minecraft/net/minecraft/data/Main.java.patch +++ b/patches/minecraft/net/minecraft/data/Main.java.patch @@ -26,7 +26,7 @@ + Collection existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).collect(Collectors.toList()); + java.util.Set mods = new java.util.HashSet<>(optionset.valuesOf(mod)); + boolean isFlat = mods.isEmpty() || optionset.has(flat); -+ net.minecraftforge.fml.ModLoader.get().runDataGenerator(mods, path, inputs, existingPacks, flag2, flag1, flag3, flag4, flag5, isFlat); ++ net.minecraftforge.fml.DatagenModLoader.begin(mods, path, inputs, existingPacks, flag2, flag1, flag3, flag4, flag5, isFlat); + if (mods.contains("minecraft") || mods.isEmpty()) + func_200264_a(isFlat ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c(); } else { diff --git a/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch b/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch index 900631fbc..6d26cb2e0 100644 --- a/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch @@ -20,7 +20,7 @@ this.field_199807_b = p_i49381_1_.toArray((p_209360_0_) -> { return new Ingredient.IItemList[p_209360_0_]; }); -+ this.isSimple = !net.minecraftforge.fml.ModLoader.isDataGenRunning() && !Arrays.stream(field_199807_b).anyMatch(list -> list.func_199799_a().stream().anyMatch(stack -> stack.func_77973_b().func_77645_m())); ++ this.isSimple = !net.minecraftforge.fml.DatagenModLoader.isRunningDataGen() && !Arrays.stream(field_199807_b).anyMatch(list -> list.func_199799_a().stream().anyMatch(stack -> stack.func_77973_b().func_77645_m())); + Ingredient.INSTANCES.add(this); } diff --git a/src/main/java/net/minecraftforge/event/AddReloadListenerEvent.java b/src/main/java/net/minecraftforge/event/AddReloadListenerEvent.java index 9b0ab446e..325acd13e 100644 --- a/src/main/java/net/minecraftforge/event/AddReloadListenerEvent.java +++ b/src/main/java/net/minecraftforge/event/AddReloadListenerEvent.java @@ -1,13 +1,18 @@ package net.minecraftforge.event; import com.google.common.collect.ImmutableList; +import net.minecraft.profiler.IProfiler; import net.minecraft.resources.DataPackRegistries; import net.minecraft.resources.IFutureReloadListener; +import net.minecraft.resources.IResourceManager; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.ModLoader; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * The main ResourceManager is recreated on each reload, through {@link DataPackRegistries}'s creation. @@ -30,7 +35,7 @@ public class AddReloadListenerEvent extends Event */ public void addListener(IFutureReloadListener listener) { - listeners.add(listener); + listeners.add(new WrappedStateAwareListener(listener)); } public List getListeners() @@ -42,4 +47,19 @@ public class AddReloadListenerEvent extends Event { return dataPackRegistries; } + private static class WrappedStateAwareListener implements IFutureReloadListener { + private final IFutureReloadListener wrapped; + + private WrappedStateAwareListener(final IFutureReloadListener wrapped) { + this.wrapped = wrapped; + } + + @Override + public CompletableFuture reload(final IStage stage, final IResourceManager resourceManager, final IProfiler preparationsProfiler, final IProfiler reloadProfiler, final Executor backgroundExecutor, final Executor gameExecutor) { + if (ModLoader.isLoadingStateValid()) + return wrapped.reload(stage, resourceManager, preparationsProfiler, reloadProfiler, backgroundExecutor, gameExecutor); + else + return CompletableFuture.completedFuture(null); + } + } } diff --git a/src/main/java/net/minecraftforge/fml/DatagenModLoader.java b/src/main/java/net/minecraftforge/fml/DatagenModLoader.java new file mode 100644 index 000000000..ca09f5125 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/DatagenModLoader.java @@ -0,0 +1,34 @@ +package net.minecraftforge.fml; + +import net.minecraft.util.registry.Bootstrap; +import net.minecraftforge.client.model.generators.ExistingFileHelper; +import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Set; + +public class DatagenModLoader { + private static final Logger LOGGER = LogManager.getLogger(); + private static GatherDataEvent.DataGeneratorConfig dataGeneratorConfig; + private static ExistingFileHelper existingFileHelper; + private static boolean runningDataGen; + + public static boolean isRunningDataGen() { + return runningDataGen; + } + + public static void begin(final Set mods, final Path path, final java.util.Collection inputs, Collection existingPacks, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, final boolean structureValidator, final boolean flat) { + if (mods.contains("minecraft") && mods.size() == 1) return; + LOGGER.info("Initializing Data Gatherer for mods {}", mods); + runningDataGen = true; + Bootstrap.register(); + dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat); + existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator); + ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{}); + ModLoader.get().runEventGenerator(mc->new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper)); + dataGeneratorConfig.runAll(); + } +} diff --git a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java index 8174ca8af..82f14bb68 100644 --- a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java +++ b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java @@ -21,11 +21,7 @@ package net.minecraftforge.fml; import static net.minecraftforge.fml.Logging.LOADING; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; @@ -33,6 +29,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent; +import net.minecraftforge.fml.javafmlmod.FMLModContainer; import org.apache.commons.lang3.time.StopWatch; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -97,6 +94,20 @@ public class DeferredWorkQueue return CompletableFuture.supplyAsync(work, r->tasks.add(new TaskInfo(modInfo, r))); } + /** + * DEPRECATED FOR REMOVAL. Use {@link ParallelDispatchEvent#enqueueWork(Runnable)} or {@link ParallelDispatchEvent#enqueueWork(Supplier)} + * + * @param workToEnqueue Runnable to execute later + * @return a completable future + */ + @Deprecated + public static CompletableFuture runLater(Runnable workToEnqueue) { + return + Optional.ofNullable(ModLoadingContext.get().getActiveContainer().modLoadingStage.getDeferredWorkQueue()) + .map(wq->wq.enqueueWork(ModLoadingContext.get().getActiveContainer().modInfo, workToEnqueue)) + .orElseGet(()->CompletableFuture.completedFuture(null)); + } + static class TaskInfo { public final IModInfo owner; diff --git a/src/main/java/net/minecraftforge/fml/ForgeI18n.java b/src/main/java/net/minecraftforge/fml/ForgeI18n.java index d5fd28773..e2758252d 100644 --- a/src/main/java/net/minecraftforge/fml/ForgeI18n.java +++ b/src/main/java/net/minecraftforge/fml/ForgeI18n.java @@ -19,6 +19,7 @@ package net.minecraftforge.fml; +import com.google.common.base.CharMatcher; import net.minecraftforge.fml.loading.StringUtils; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import org.apache.commons.lang3.text.ExtendedMessageFormat; @@ -33,14 +34,22 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; +import java.util.regex.Pattern; import static net.minecraftforge.fml.Logging.CORE; //TODO, this should be re-evaluated now that ITextComponents are passed everywhere instaed of strings. public class ForgeI18n { private static final Logger LOGGER = LogManager.getLogger(); + // From FontRenderer.renderCharAtPos + private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"; + private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate(); private static Map i18n; private static Map customFactories; + // From StringUtils + private static final Pattern PATTERN_CONTROL_CODE = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); + + static { customFactories = new HashMap<>(); @@ -102,6 +111,16 @@ public class ForgeI18n { return extendedMessageFormat.format(args); } + public static String stripSpecialChars(String message) + { + // We can't handle many unicode points in the splash renderer + return DISALLOWED_CHAR_MATCHER.removeFrom(stripControlCodes(message)); + } + + public static String stripControlCodes(String text) { + return PATTERN_CONTROL_CODE.matcher(text).replaceAll(""); + } + public static class CustomReadOnlyFormat extends Format { private final BiConsumer formatter; diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index c1c7c2b43..ccb7db510 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -112,8 +112,7 @@ public class ModLoader private final List loadingExceptions; private final List loadingWarnings; - private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig; - private ExistingFileHelper existingFileHelper; + private boolean loadingStateValid; @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private final Optional> statusConsumer = StartupMessageManager.modLoaderConsumer(); @@ -162,6 +161,7 @@ public class ModLoader * @param periodicTask Optional periodic task to perform on the main thread while other activities run */ public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) { + loadingStateValid = true; statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete")); FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask); statusConsumer.ifPresent(c->c.accept("Loading mods")); @@ -174,6 +174,7 @@ public class ModLoader if (!this.loadingExceptions.isEmpty()) { LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0)); modList.setLoadedMods(Collections.emptyList()); + loadingStateValid = false; throw new LoadingFailedException(loadingExceptions); } statusConsumer.ifPresent(c->c.accept("Building Mod List")); @@ -185,6 +186,7 @@ public class ModLoader if (!loadingExceptions.isEmpty()) { LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0)); modList.setLoadedMods(Collections.emptyList()); + loadingStateValid = false; throw new LoadingFailedException(loadingExceptions); } modList.setLoadedMods(modContainers); @@ -225,21 +227,29 @@ public class ModLoader } private void dispatchAndHandleError(ModLoadingStage state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker) { + if (!isLoadingStateValid()) { + LOGGER.error("Cowardly refusing to process mod state change request from {}", state); + return; + } waitForTransition(state, syncExecutor, ticker, state.buildTransition(syncExecutor, parallelExecutor)); } private void dispatchAndHandleError(ModLoadingStage state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker, Function> preSyncTask, Function> postSyncTask) { + if (!isLoadingStateValid()) { + LOGGER.error("Cowardly refusing to process mod state change request from {}", state); + return; + } waitForTransition(state, syncExecutor, ticker, state.buildTransition(syncExecutor, parallelExecutor, preSyncTask, postSyncTask)); } private void waitForTransition(final ModLoadingStage state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker, final CompletableFuture> transition) { while (!transition.isDone()) { - ticker.run(); - syncExecutor.drive(); + syncExecutor.drive(ticker); } try { transition.join(); } catch (CompletionException e) { + loadingStateValid = false; Throwable t = e.getCause(); final List notModLoading = Arrays.stream(t.getSuppressed()) .filter(obj -> !(obj instanceof ModLoadingException)) @@ -292,7 +302,27 @@ public class ModLoader } } + /** + * @return If the current mod loading state is valid. Use if you interact with vanilla systems directly during loading + * and don't want to cause extraneous crashes due to trying to do things that aren't possible in a "broken load" + */ + public static boolean isLoadingStateValid() { + return get().loadingStateValid; + } + + public void runEventGenerator(Function generator) { + if (!loadingStateValid) { + LOGGER.error("Cowardly refusing to send event generator to a broken mod state"); + return; + } + ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(generator.apply(mc))); + } + public void postEvent(T e) { + if (!loadingStateValid) { + LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName()); + return; + } ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e)); } @@ -311,20 +341,4 @@ public class ModLoader public static boolean isDataGenRunning () { return runningDataGen; } - - public void runDataGenerator(final Set mods, final Path path, final Collection inputs, Collection existingPacks, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, final boolean structureValidator, final boolean flat) { -// if (mods.contains("minecraft") && mods.size() == 1) return; -// LOGGER.info("Initializing Data Gatherer for mods {}", mods); -// runningDataGen = true; -// Bootstrap.register(); -// dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat); -// existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator); -// gatherAndInitializeMods(() -> {}); -// dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {}); -// dataGeneratorConfig.runAll(); - } - - public Function getDataGeneratorEvent() { - return mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper); - } } diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingContext.java b/src/main/java/net/minecraftforge/fml/ModLoadingContext.java index 23dbd81a9..20bcb0968 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingContext.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingContext.java @@ -28,6 +28,7 @@ public class ModLoadingContext { private static ThreadLocal context = ThreadLocal.withInitial(ModLoadingContext::new); private Object languageExtension; + private ModLoadingStage stage; public static ModLoadingContext get() { return context.get(); diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingException.java b/src/main/java/net/minecraftforge/fml/ModLoadingException.java index 3547cef17..73fb4a972 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingException.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingException.java @@ -20,7 +20,9 @@ package net.minecraftforge.fml; import com.google.common.collect.Streams; +import net.minecraft.util.StringUtils; import net.minecraftforge.fml.loading.EarlyLoadingException; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.forgespi.language.IModInfo; import java.util.Arrays; @@ -79,4 +81,11 @@ public class ModLoadingException extends RuntimeException public String getMessage() { return formatToString(); } + + public IModInfo getModInfo() { + return modInfo; + } + public String getCleanMessage() { + return ForgeI18n.stripControlCodes(formatToString()); + } } diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java index 157cdb7d8..0ac88395e 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java @@ -57,14 +57,14 @@ public enum ModLoadingStage ENQUEUE_IMC(InterModEnqueueEvent.class), PROCESS_IMC(InterModProcessEvent.class), COMPLETE(FMLLoadCompleteEvent.class), - DONE(), - GATHERDATA(); + DONE(); private final Supplier>> eventFunctionStream; private final EventDispatcher eventManager; private final Optional> parallelEventClass; private final ThreadSelector threadSelector; private final BiFunction>, CompletableFuture>> finalActivityGenerator; + private DeferredWorkQueue deferredWorkQueue; ModLoadingStage(Class parallelClass) { final EventGenerator event = EventGenerator.fromFunction(LamdbaExceptionUtils.rethrowFunction((ModContainer mc) -> parallelClass.getConstructor(ModContainer.class).newInstance(mc))); @@ -72,7 +72,7 @@ public enum ModLoadingStage this.threadSelector = ThreadSelector.PARALLEL; this.eventManager = EventDispatcher.identity(); this.parallelEventClass = Optional.of(parallelClass); - final DeferredWorkQueue deferredWorkQueue = new DeferredWorkQueue(this, parallelClass); + deferredWorkQueue = new DeferredWorkQueue(this, parallelClass); this.finalActivityGenerator = (e, prev) -> prev.thenApplyAsync((List t) -> { deferredWorkQueue.runTasks(); return t; @@ -145,6 +145,10 @@ public enum ModLoadingStage } } + public DeferredWorkQueue getDeferredWorkQueue() { + return deferredWorkQueue; + } + public interface EventGenerator extends Function { static EventGenerator fromFunction(Function fn) { return fn::apply; diff --git a/src/main/java/net/minecraftforge/fml/ModWorkManager.java b/src/main/java/net/minecraftforge/fml/ModWorkManager.java index 1a0765f78..f0a37a96c 100644 --- a/src/main/java/net/minecraftforge/fml/ModWorkManager.java +++ b/src/main/java/net/minecraftforge/fml/ModWorkManager.java @@ -17,10 +17,10 @@ public class ModWorkManager { boolean selfDriven(); boolean driveOne(); - default void drive() { + default void drive(Runnable ticker) { if (!selfDriven()) { while (driveOne()) - ; + ticker.run(); } } } diff --git a/src/main/java/net/minecraftforge/fml/SidedProvider.java b/src/main/java/net/minecraftforge/fml/SidedProvider.java deleted file mode 100644 index 230c17978..000000000 --- a/src/main/java/net/minecraftforge/fml/SidedProvider.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.fml; - -import net.minecraft.client.Minecraft; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.fml.client.ClientHooks; -import net.minecraftforge.fml.loading.FMLEnvironment; - -import java.util.function.Function; -import java.util.function.Supplier; - -public enum SidedProvider -{ - // All of these need to be careful not to directly dereference the client and server elements in their signatures - DATAFIXER( - c->c.get().getDataFixer(), - s->s.get().getDataFixer(), - ()-> { throw new UnsupportedOperationException(); }), - STRIPCHARS( - (Function, Function>)c-> ClientHooks::stripSpecialChars, - s-> str->str, - ()-> str->str); - - private static Supplier client; - private static Supplier server; - - public static void setClient(Supplier client) - { - SidedProvider.client = client; - } - public static void setServer(Supplier server) - { - SidedProvider.server = server; - } - - private final Function, ?> clientSide; - private final Function, ?> serverSide; - private final Supplier testSide; - - SidedProvider(Function, T> clientSide, Function, T> serverSide, Supplier testSide) - { - this.clientSide = clientSide; - this.serverSide = serverSide; - this.testSide = testSide; - } - - @SuppressWarnings("unchecked") - public T get() { - if (FMLEnvironment.dist == Dist.CLIENT) { - return (T)this.clientSide.apply(client); - } - else if (FMLEnvironment.dist == Dist.DEDICATED_SERVER) { - return (T)this.serverSide.apply(server); - } - else { - return (T)this.testSide.get(); - } - } -} diff --git a/src/main/java/net/minecraftforge/fml/client/ClientHooks.java b/src/main/java/net/minecraftforge/fml/client/ClientHooks.java index 79093cc68..28cdc0e3b 100644 --- a/src/main/java/net/minecraftforge/fml/client/ClientHooks.java +++ b/src/main/java/net/minecraftforge/fml/client/ClientHooks.java @@ -75,9 +75,6 @@ public class ClientHooks { private static final Logger LOGGER = LogManager.getLogger(); private static final Marker CLIENTHOOKS = MarkerManager.getMarker("CLIENTHOOKS"); - // From FontRenderer.renderCharAtPos - private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"; - private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate(); private static final ResourceLocation iconSheet = new ResourceLocation(ForgeVersion.MOD_ID, "textures/gui/icons.png"); @Nullable @@ -197,12 +194,6 @@ public class ClientHooks } - public static String stripSpecialChars(String message) - { - // We can't handle many unicode points in the splash renderer - return DISALLOWED_CHAR_MATCHER.removeFrom(net.minecraft.util.StringUtils.stripControlCodes(message)); - } - private static SetMultimap missingTextures = HashMultimap.create(); private static Set badTextureDomains = Sets.newHashSet(); private static Table> brokenTextures = HashBasedTable.create(); diff --git a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java index 5bd076255..8597706cc 100644 --- a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java +++ b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java @@ -22,32 +22,40 @@ package net.minecraftforge.fml.client; import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.loading.LogMarkers.LOADING; +import java.io.File; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; import net.minecraft.resources.IFutureReloadListener; import net.minecraft.resources.IPackNameDecorator; import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IResourceManager; import net.minecraft.resources.ResourcePackInfo; import net.minecraft.resources.ResourcePackList; +import net.minecraft.util.datafix.codec.DatapackCodec; import net.minecraftforge.fml.BrandingControl; import net.minecraftforge.fml.LoadingFailedException; import net.minecraftforge.fml.LogicalSidedProvider; +import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.ModLoadingWarning; import net.minecraftforge.fml.ModWorkManager; -import net.minecraftforge.fml.SidedProvider; import net.minecraftforge.fml.VersionChecker; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -80,22 +88,41 @@ public class ClientModLoader private static final Logger LOGGER = LogManager.getLogger(); private static boolean loading; private static Minecraft mc; + private static boolean loadingComplete; private static LoadingFailedException error; private static EarlyLoaderGUI earlyLoaderGUI; + private static class SpacedRunnable implements Runnable { + static final long NANO_SLEEP_TIME = TimeUnit.MILLISECONDS.toNanos(50); + private final Runnable wrapped; + private long lastRun; + + private SpacedRunnable(final Runnable wrapped) { + this.wrapped = wrapped; + this.lastRun = System.nanoTime() - NANO_SLEEP_TIME; + } + + @Override + public void run() { + if (System.nanoTime() - this.lastRun > NANO_SLEEP_TIME) { + wrapped.run(); + this.lastRun = System.nanoTime(); + } + } + } public static void begin(final Minecraft minecraft, final ResourcePackList defaultResourcePacks, final IReloadableResourceManager mcResourceManager, DownloadingPackFinder metadataSerializer) { // force log4j to shutdown logging in a shutdown hook. This is because we disable default shutdown hook so the server properly logs it's shutdown Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown)); loading = true; ClientModLoader.mc = minecraft; - SidedProvider.setClient(()->minecraft); LogicalSidedProvider.setClient(()->minecraft); LanguageHook.loadForgeAndMCLangs(); earlyLoaderGUI = new EarlyLoaderGUI(minecraft.getMainWindow()); - createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), earlyLoaderGUI::renderTick)).run(); + createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), new SpacedRunnable(earlyLoaderGUI::renderTick))).run(); if (error == null) { ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder); + DatapackCodec.field_234880_a_.addModPacks(ResourcePackLoader.getPackNames()); mcResourceManager.addReloadListener(ClientModLoader::onResourceReload); mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener()); ModelLoaderRegistry.init(); @@ -110,10 +137,10 @@ public class ClientModLoader private static Runnable createRunnableWithCatch(Runnable r) { return ()-> { + if (loadingComplete) return; try { r.run(); } catch (LoadingFailedException e) { - MinecraftForge.EVENT_BUS.shutdown(); if (error == null) error = e; } }; @@ -121,7 +148,7 @@ public class ClientModLoader private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) { earlyLoaderGUI.handleElsewhere(); - createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, executor -> CompletableFuture.runAsync(ClientModLoader::preSidedRunnable, executor), executor -> CompletableFuture.runAsync(ClientModLoader::postSidedRunnable, executor), ()->{})).run(); + createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, executor -> CompletableFuture.runAsync(ClientModLoader::preSidedRunnable, executor), executor -> CompletableFuture.runAsync(ClientModLoader::postSidedRunnable, executor), new SpacedRunnable(earlyLoaderGUI::renderTick))).run(); } private static void postSidedRunnable() { @@ -135,15 +162,15 @@ public class ClientModLoader private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) { - createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, ()->{})).run(); + createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, new SpacedRunnable(earlyLoaderGUI::renderTick))).run(); loading = false; + loadingComplete = true; // reload game settings on main thread syncExecutor.execute(()->mc.gameSettings.loadOptions()); } public static VersionChecker.Status checkForUpdates() { - boolean anyOutdated = ModList.get().getMods().stream() .map(VersionChecker::getResult) .map(result -> result.status) @@ -173,6 +200,24 @@ public class ClientModLoader if (error == null) { // We can finally start the forge eventbus up MinecraftForge.EVENT_BUS.start(); + } else { + final CrashReport crashReport = CrashReport.makeCrashReport(new Exception("Mod Loading has failed"), "Mod loading error has occurred"); + error.getErrors().forEach(mle -> { + final CrashReportCategory category = crashReport.makeCategory(mle.getModInfo().getModId()); + category.applyStackTrace(mle.getCause()); + category.addDetail("Failure message", mle.getCleanMessage()); + category.addDetail("Exception message", mle.getCause().toString()); + category.addDetail("Mod Version", mle.getModInfo().getVersion().toString()); + category.addDetail("Mod Issue URL", ((ModFileInfo)mle.getModInfo().getOwningFile()).getConfigElement("issueTrackerURL").orElse("NOT PROVIDED")); + }); + final File file1 = new File(mc.gameDir, "crash-reports"); + final File file2 = new File(file1, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); + if (crashReport.saveToFile(file2)) { + LOGGER.fatal("Crash report saved to {}", crashReport.getFile()); + } else { + LOGGER.fatal("Failed to save crash report"); + } + System.out.print(crashReport.getCompleteReport()); } if (error != null || !warnings.isEmpty()) { mc.displayGuiScreen(new LoadingErrorScreen(error, warnings)); diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/GatherDataEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/GatherDataEvent.java index 54620d2fb..cc0051fa3 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/GatherDataEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/GatherDataEvent.java @@ -22,6 +22,7 @@ package net.minecraftforge.fml.event.lifecycle; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraft.data.DataGenerator; import net.minecraftforge.client.model.generators.ExistingFileHelper; +import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.ModContainer; import java.nio.file.Path; @@ -33,19 +34,25 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -public class GatherDataEvent extends ModLifecycleEvent +public class GatherDataEvent extends Event implements IModBusEvent { private final DataGenerator dataGenerator; private final DataGeneratorConfig config; private final ExistingFileHelper existingFileHelper; - public GatherDataEvent(final ModContainer modContainer, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper) + private final ModContainer modContainer; + + public GatherDataEvent(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper) { - super(modContainer); + this.modContainer = mc; this.dataGenerator = dataGenerator; this.config = dataGeneratorConfig; this.existingFileHelper = existingFileHelper; } + public ModContainer getModContainer() { + return this.modContainer; + } + public DataGenerator getGenerator() { return this.dataGenerator; } public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; } public boolean includeServer() { return this.config.server; } diff --git a/src/main/java/net/minecraftforge/fml/packs/ResourcePackLoader.java b/src/main/java/net/minecraftforge/fml/packs/ResourcePackLoader.java index 35c401d20..c2b80c68b 100644 --- a/src/main/java/net/minecraftforge/fml/packs/ResourcePackLoader.java +++ b/src/main/java/net/minecraftforge/fml/packs/ResourcePackLoader.java @@ -54,16 +54,16 @@ public class ResourcePackLoader public static void loadResourcePacks(ResourcePackList resourcePacks, BiFunction, BiConsumer, IPackInfoFinder> packFinder) { resourcePackList = resourcePacks; - modResourcePacks = ModList.get().getModFiles().stream(). - filter(mf->!Objects.equals(mf.getModLoader(),"minecraft")). - map(mf -> new ModFileResourcePack(mf.getFile())). - collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity(), (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new)); + modResourcePacks = ModList.get().getModFiles().stream() + .filter(mf->!Objects.equals(mf.getModLoader(),"minecraft")) + .map(mf -> new ModFileResourcePack(mf.getFile())) + .collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity(), (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new)); resourcePacks.addPackFinder(new LambdaFriendlyPackFinder(packFinder.apply(modResourcePacks, ModFileResourcePack::setPackInfo))); } public static List getPackNames() { - return modResourcePacks.values().stream().map(pack -> pack.getPackInfo().getName()).collect(Collectors.toList()); + return ModList.get().applyForEachModFile(mf->"mod:"+mf.getModInfos().get(0).getModId()).filter(n->!n.equals("mod:minecraft")).collect(Collectors.toList()); } public static Comparator> getSorter() { diff --git a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index 107ab5d04..71f739801 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -31,14 +31,12 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import net.minecraft.resources.IPackNameDecorator; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.world.storage.FolderName; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.ModLoadingWarning; -import net.minecraftforge.fml.SidedProvider; import net.minecraftforge.fml.network.ConnectionType; import net.minecraftforge.fml.network.FMLNetworkConstants; import net.minecraftforge.fml.network.FMLStatusPing; @@ -92,7 +90,6 @@ public class ServerLifecycleHooks currentServer = server; currentServer.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data // on the dedi server we need to force the stuff to setup properly - DistExecutor.unsafeRunWhenOn(Dist.DEDICATED_SERVER, ()->()->SidedProvider.setServer(()->(DedicatedServer)server)); LogicalSidedProvider.setServer(()->server); ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server)); return !MinecraftForge.EVENT_BUS.post(new FMLServerAboutToStartEvent(server)); @@ -185,30 +182,6 @@ public class ServerLifecycleHooks public static void handleExit(int retVal) { -/* - CountDownLatch latch = exitLatch; - - if (latch != null) - { - try - { - LOGGER.info("Waiting for the server to terminate/save."); - if (!latch.await(10, TimeUnit.SECONDS)) - { - LOGGER.warn("The server didn't stop within 10 seconds, exiting anyway."); - } - else - { - LOGGER.info("Server terminated."); - } - } - catch (InterruptedException e) - { - LOGGER.warn("Interrupted wait, exiting."); - } - } - -*/ System.exit(retVal); } @@ -235,5 +208,4 @@ public class ServerLifecycleHooks consumer.accept(packInfo); } } - } diff --git a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java index 8e17b9b55..b276fadd0 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java @@ -19,17 +19,23 @@ package net.minecraftforge.fml.server; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.LoadingFailedException; import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingWarning; import net.minecraftforge.fml.ModWorkManager; -import net.minecraftforge.fml.SidedProvider; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; +import java.util.concurrent.CompletableFuture; import static net.minecraftforge.fml.loading.LogMarkers.LOADING; @@ -39,20 +45,34 @@ public class ServerModLoader private static boolean hasErrors = false; public static void load() { - SidedProvider.setServer(()-> { - throw new IllegalStateException("Unable to access server yet"); - }); LogicalSidedProvider.setServer(()-> { throw new IllegalStateException("Unable to access server yet"); }); LanguageHook.loadForgeAndMCLangs(); try { ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{}); - ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), null, null, ()->{}); + ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), e-> CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e), ()->{}); ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{}); - } catch (LoadingFailedException e) { + } catch (LoadingFailedException error) { ServerModLoader.hasErrors = true; - throw e; + final CrashReport crashReport = CrashReport.makeCrashReport(error, "Mod loading error has occurred"); + error.getErrors().forEach(mle -> { + final CrashReportCategory category = crashReport.makeCategory(mle.getModInfo().getModId()); + category.applyStackTrace(mle.getCause()); + category.addDetail("Failure message", mle.getCleanMessage()); + category.addDetail("Exception message", mle.getCause().toString()); + category.addDetail("Mod Version", mle.getModInfo().getVersion().toString()); + category.addDetail("Mod Issue URL", ((ModFileInfo)mle.getModInfo().getOwningFile()).getConfigElement("issueTrackerURL").orElse("NOT PROVIDED")); + }); + final File file1 = new File("crash-reports"); + final File file2 = new File(file1, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt"); + if (crashReport.saveToFile(file2)) { + LOGGER.fatal("Crash report saved to {}", file2); + } else { + LOGGER.fatal("Failed to save crash report"); + } + System.out.print(crashReport.getCompleteReport()); + throw error; } List warnings = ModLoader.get().getWarnings(); if (!warnings.isEmpty()) {