Merge branch '1.16.1' into 1.16.x

# Conflicts:
#	patches/minecraft/net/minecraft/client/Minecraft.java.patch
#	patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch
#	src/main/java/net/minecraftforge/fml/client/ClientModLoader.java
#	src/main/java/net/minecraftforge/registries/GameData.java
This commit is contained in:
cpw 2020-08-31 21:15:56 -04:00
commit bfeec0c7bd
No known key found for this signature in database
GPG key ID: 8EB3DF749553B1B7
38 changed files with 803 additions and 814 deletions

View file

@ -29,7 +29,6 @@
this.field_110451_am = new SimpleReloadableResourceManager(ResourcePackType.CLIENT_RESOURCES); 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); + 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(); 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_71474_y.func_198017_a(this.field_110448_aq);
this.field_135017_as = new LanguageManager(this.field_71474_y.field_74363_ab); this.field_135017_as = new LanguageManager(this.field_71474_y.field_74363_ab);
this.field_110451_am.func_219534_a(this.field_135017_as); this.field_110451_am.func_219534_a(this.field_135017_as);

View file

@ -13,3 +13,14 @@
return this.field_85075_d.length; 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);

View file

@ -26,7 +26,7 @@
+ Collection<Path> existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).collect(Collectors.toList()); + Collection<Path> existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).collect(Collectors.toList());
+ java.util.Set<String> mods = new java.util.HashSet<>(optionset.valuesOf(mod)); + java.util.Set<String> mods = new java.util.HashSet<>(optionset.valuesOf(mod));
+ boolean isFlat = mods.isEmpty() || optionset.has(flat); + 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()) + if (mods.contains("minecraft") || mods.isEmpty())
+ func_200264_a(isFlat ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c(); + func_200264_a(isFlat ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c();
} else { } else {

View file

@ -20,7 +20,7 @@
this.field_199807_b = p_i49381_1_.toArray((p_209360_0_) -> { this.field_199807_b = p_i49381_1_.toArray((p_209360_0_) -> {
return new Ingredient.IItemList[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().isDamageable(stack))); + this.isSimple = !net.minecraftforge.fml.DatagenModLoader.isDataGenRunning() && !Arrays.stream(field_199807_b).anyMatch(list -> list.func_199799_a().stream().anyMatch(stack -> stack.func_77973_b().isDamageable(stack)));
+ Ingredient.INSTANCES.add(this); + Ingredient.INSTANCES.add(this);
} }

View file

@ -79,7 +79,7 @@ public class ModFile implements IModFile {
this.locator = locator; this.locator = locator;
this.filePath = file; this.filePath = file;
this.parser = parser; this.parser = parser;
manifest = locator.findManifest(file).orElse(DEFAULTMANIFEST); manifest = Optional.ofNullable(locator).flatMap(l->l.findManifest(file)).orElse(DEFAULTMANIFEST);
if (manifest != DEFAULTMANIFEST) LOGGER.debug(SCAN,"Mod file {} has a manifest", file); if (manifest != DEFAULTMANIFEST) LOGGER.debug(SCAN,"Mod file {} has a manifest", file);
else LOGGER.debug(SCAN,"Mod file {} is missing a manifest", file); else LOGGER.debug(SCAN,"Mod file {} is missing a manifest", file);
final Optional<String> value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE)); final Optional<String> value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE));

View file

@ -23,16 +23,22 @@ import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.RangedAttribute; import net.minecraft.entity.ai.attributes.RangedAttribute;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.world.storage.IServerConfiguration; import net.minecraft.world.storage.IServerConfiguration;
import net.minecraft.world.storage.IWorldInfo;
import net.minecraft.world.storage.SaveFormat; import net.minecraft.world.storage.SaveFormat;
import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.*; import net.minecraftforge.fml.*;
import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLModIdMappingEvent; import net.minecraftforge.fml.event.lifecycle.FMLModIdMappingEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.progress.StartupMessageManager; import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.server.command.ConfigCommand;
import net.minecraftforge.server.command.ForgeCommand;
import net.minecraftforge.versions.forge.ForgeVersion; import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion; import net.minecraftforge.versions.mcp.MCPVersion;
@ -106,7 +112,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook
final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(this::preInit); modEventBus.addListener(this::preInit);
modEventBus.addListener(this::gatherData); modEventBus.addListener(this::gatherData);
modEventBus.addGenericListener(IRecipeSerializer.class, this::registerRecipeSerialziers); modEventBus.register(this);
ATTRIBUTES.register(modEventBus); ATTRIBUTES.register(modEventBus);
MinecraftForge.EVENT_BUS.addListener(this::serverStopping); MinecraftForge.EVENT_BUS.addListener(this::serverStopping);
MinecraftForge.EVENT_BUS.addGenericListener(SoundEvent.class, this::missingSoundMapping); MinecraftForge.EVENT_BUS.addGenericListener(SoundEvent.class, this::missingSoundMapping);
@ -119,6 +125,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook
MinecraftForge.EVENT_BUS.addListener(VillagerTradingManager::loadTrades); MinecraftForge.EVENT_BUS.addListener(VillagerTradingManager::loadTrades);
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER); MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
MinecraftForge.EVENT_BUS.register(this);
} }
public void preInit(FMLCommonSetupEvent evt) public void preInit(FMLCommonSetupEvent evt)
@ -208,6 +215,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook
} }
} }
@SubscribeEvent //ModBus, can't use addListener due to nested genetics.
public void registerRecipeSerialziers(RegistryEvent.Register<IRecipeSerializer<?>> event) public void registerRecipeSerialziers(RegistryEvent.Register<IRecipeSerializer<?>> event)
{ {
CraftingHelper.register(AndCondition.Serializer.INSTANCE); CraftingHelper.register(AndCondition.Serializer.INSTANCE);

View file

@ -20,13 +20,18 @@
package net.minecraftforge.event; package net.minecraftforge.event;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import net.minecraft.profiler.IProfiler;
import net.minecraft.resources.DataPackRegistries; import net.minecraft.resources.DataPackRegistries;
import net.minecraft.resources.IFutureReloadListener; import net.minecraft.resources.IFutureReloadListener;
import net.minecraft.resources.IResourceManager;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.ModLoader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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. * The main ResourceManager is recreated on each reload, through {@link DataPackRegistries}'s creation.
@ -49,7 +54,7 @@ public class AddReloadListenerEvent extends Event
*/ */
public void addListener(IFutureReloadListener listener) public void addListener(IFutureReloadListener listener)
{ {
listeners.add(listener); listeners.add(new WrappedStateAwareListener(listener));
} }
public List<IFutureReloadListener> getListeners() public List<IFutureReloadListener> getListeners()
@ -61,4 +66,19 @@ public class AddReloadListenerEvent extends Event
{ {
return dataPackRegistries; return dataPackRegistries;
} }
private static class WrappedStateAwareListener implements IFutureReloadListener {
private final IFutureReloadListener wrapped;
private WrappedStateAwareListener(final IFutureReloadListener wrapped) {
this.wrapped = wrapped;
}
@Override
public CompletableFuture<Void> 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);
}
}
} }

View file

@ -46,6 +46,7 @@ public class RegistryEvent<T extends IForgeRegistryEntry<T>> extends GenericEven
*/ */
public static class NewRegistry extends net.minecraftforge.eventbus.api.Event implements IModBusEvent public static class NewRegistry extends net.minecraftforge.eventbus.api.Event implements IModBusEvent
{ {
public NewRegistry(ModContainer mc) {}
@Override @Override
public String toString() { public String toString() {
return "RegistryEvent.NewRegistry"; return "RegistryEvent.NewRegistry";

View file

@ -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<String> mods, final Path path, final java.util.Collection<Path> inputs, Collection<Path> 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();
}
}

View file

@ -21,23 +21,19 @@ package net.minecraftforge.fml;
import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.LOADING;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent;
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
import org.apache.commons.lang3.time.StopWatch; import org.apache.commons.lang3.time.StopWatch;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import com.google.common.base.Function;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.IModInfo;
@ -52,166 +48,74 @@ import net.minecraftforge.forgespi.language.IModInfo;
* <p> * <p>
* Exceptions from tasks will be handled gracefully, causing a mod loading * Exceptions from tasks will be handled gracefully, causing a mod loading
* error. Tasks that take egregiously long times to run will be logged. * error. Tasks that take egregiously long times to run will be logged.
*
* This is being deprecated in favour of a new interface on loading events, to remove confusion about how it operates. #TODO
*/ */
@Deprecated
public class DeferredWorkQueue public class DeferredWorkQueue
{ {
private static class TaskInfo
{
public final IModInfo owner;
public final Runnable task;
TaskInfo(IModInfo owner, Runnable task) {
this.owner = owner;
this.task = task;
}
}
/**
* {@link Runnable} except it allows throwing checked exceptions.
*
* Is to {@link Runnable} as {@link Callable} is to {@link Supplier}.
*/
@FunctionalInterface
public interface CheckedRunnable
{
void run() throws Exception;
}
private static final Logger LOGGER = LogManager.getLogger(); private static final Logger LOGGER = LogManager.getLogger();
private static ThreadLocal<ModContainer> currentOwner = new ThreadLocal<>(); private static Map<Class<? extends ParallelDispatchEvent>, DeferredWorkQueue> workQueues = new HashMap<>();
private static List<ModLoadingException> raisedExceptions = new ArrayList<>();
private static final ConcurrentLinkedDeque<TaskInfo> taskQueue = new ConcurrentLinkedDeque<>(); private final ModLoadingStage modLoadingStage;
private static final Executor deferredExecutor = r -> taskQueue.add(new TaskInfo(currentOwner.get().getModInfo(), r)); private final ConcurrentLinkedDeque<TaskInfo> tasks = new ConcurrentLinkedDeque<>();
private static <T> Function<Throwable, T> handleException() { public DeferredWorkQueue(final ModLoadingStage modLoadingStage, Class<? extends ParallelDispatchEvent> eventClass) {
final ModContainer owner = currentOwner.get(); this.modLoadingStage = modLoadingStage;
return t -> { workQueues.put(eventClass, this);
LogManager.getLogger(DeferredWorkQueue.class).error("Encountered exception executing deferred work", t);
raisedExceptions.add(new ModLoadingException(owner.getModInfo(), owner.getCurrentState(), "fml.modloading.failedtoprocesswork", t));
return null;
};
} }
/** public static Optional<DeferredWorkQueue> lookup(Optional<Class<? extends ParallelDispatchEvent>> parallelClass) {
* Run a task on the loading thread at the next available opportunity, i.e. return Optional.ofNullable(workQueues.get(parallelClass.orElse(null)));
* after the current lifecycle event has completed.
* <p>
* If the task must throw a checked exception, use
* {@link #runLaterChecked(CheckedRunnable)}.
* <p>
* If the task has a result, use {@link #getLater(Supplier)} or
* {@link #getLaterChecked(Callable)}.
*
* @param workToEnqueue A {@link Runnable} to execute later, on the loading
* thread
* @return A {@link CompletableFuture} that completes at said time
*/
public static CompletableFuture<Void> runLater(Runnable workToEnqueue) {
currentOwner.set(ModLoadingContext.get().getActiveContainer());
return CompletableFuture.runAsync(workToEnqueue, deferredExecutor).exceptionally(DeferredWorkQueue.handleException());
} }
/** void runTasks() {
* Run a task on the loading thread at the next available opportunity, i.e. if (tasks.isEmpty()) return;
* after the current lifecycle event has completed. This variant allows the task LOGGER.debug(LOADING, "Dispatching synchronous work after {}: {} jobs", modLoadingStage, tasks.size());
* to throw a checked exception.
* <p>
* If the task does not throw a checked exception, use
* {@link #runLater(Runnable)}.
* <p>
* If the task has a result, use {@link #getLater(Supplier)} or
* {@link #getLaterChecked(Callable)}.
*
* @param workToEnqueue A {@link CheckedRunnable} to execute later, on the
* loading thread
* @return A {@link CompletableFuture} that completes at said time
*/
public static CompletableFuture<Void> runLaterChecked(CheckedRunnable workToEnqueue) {
return runLater(() -> {
try {
workToEnqueue.run();
} catch (Throwable t) {
throw new CompletionException(t);
}
});
}
/**
* Run a task computing a result on the loading thread at the next available
* opportunity, i.e. after the current lifecycle event has completed.
* <p>
* If the task throws a checked exception, use
* {@link #getLaterChecked(Callable)}.
* <p>
* If the task does not have a result, use {@link #runLater(Runnable)} or
* {@link #runLaterChecked(CheckedRunnable)}.
*
* @param <T> The result type of the task
* @param workToEnqueue A {@link Supplier} to execute later, on the loading
* thread
* @return A {@link CompletableFuture} that completes at said time
*/
public static <T> CompletableFuture<T> getLater(Supplier<T> workToEnqueue) {
currentOwner.set(ModLoadingContext.get().getActiveContainer());
return CompletableFuture.supplyAsync(workToEnqueue, deferredExecutor).exceptionally(DeferredWorkQueue.handleException());
}
/**
* Run a task computing a result on the loading thread at the next available
* opportunity, i.e. after the current lifecycle event has completed. This
* variant allows the task to throw a checked exception.
* <p>
* If the task does not throw a checked exception, use
* {@link #getLater(Callable)}.
* <p>
* If the task does not have a result, use {@link #runLater(Runnable)} or
* {@link #runLaterChecked(CheckedRunnable)}.
*
* @param <T> The result type of the task
* @param workToEnqueue A {@link Supplier} to execute later, on the loading
* thread
* @return A {@link CompletableFuture} that completes at said time
*/
public static <T> CompletableFuture<T> getLaterChecked(Callable<T> workToEnqueue) {
return getLater(() -> {
try {
return workToEnqueue.call();
} catch (Throwable t) {
throw new CompletionException(t);
}
});
}
static void clear() {
taskQueue.clear();
}
static Executor workExecutor = Runnable::run;
static void runTasks(ModLoadingStage fromStage, Consumer<List<ModLoadingException>> errorHandler) {
raisedExceptions.clear();
if (taskQueue.isEmpty()) return; // Don't log unnecessarily
LOGGER.info(LOADING, "Dispatching synchronous work after {}: {} jobs", fromStage, taskQueue.size());
StopWatch globalTimer = StopWatch.createStarted(); StopWatch globalTimer = StopWatch.createStarted();
final CompletableFuture<Void> tasks = CompletableFuture.allOf(taskQueue.stream().map(ti -> makeRunnable(ti, workExecutor)).toArray(CompletableFuture[]::new)); tasks.forEach(t->makeRunnable(t, Runnable::run));
tasks.join(); LOGGER.debug(LOADING, "Synchronous work queue completed in {}", globalTimer);
LOGGER.info(LOADING, "Synchronous work queue completed in {}", globalTimer);
errorHandler.accept(raisedExceptions);
} }
private static CompletableFuture<?> makeRunnable(TaskInfo ti, Executor executor) { private static void makeRunnable(TaskInfo ti, Executor executor) {
return CompletableFuture.runAsync(() -> { executor.execute(() -> {
Stopwatch timer = Stopwatch.createStarted(); Stopwatch timer = Stopwatch.createStarted();
ti.task.run(); ti.task.run();
timer.stop(); timer.stop();
if (timer.elapsed(TimeUnit.SECONDS) >= 1) { if (timer.elapsed(TimeUnit.SECONDS) >= 1) {
LOGGER.warn(LOADING, "Mod '{}' took {} to run a deferred task.", ti.owner.getModId(), timer); LOGGER.warn(LOADING, "Mod '{}' took {} to run a deferred task.", ti.owner.getModId(), timer);
} }
}, executor); });
}
public CompletableFuture<Void> enqueueWork(final IModInfo modInfo, final Runnable work) {
return CompletableFuture.runAsync(work, r->tasks.add(new TaskInfo(modInfo, r)));
}
public <T> CompletableFuture<T> enqueueWork(final IModInfo modInfo, final Supplier<T> work) {
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<Void> 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;
public final Runnable task;
private TaskInfo(IModInfo owner, Runnable task) {
this.owner = owner;
this.task = task;
}
} }
} }

View file

@ -19,6 +19,7 @@
package net.minecraftforge.fml; package net.minecraftforge.fml;
import com.google.common.base.CharMatcher;
import net.minecraftforge.fml.loading.StringUtils; import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.commons.lang3.text.ExtendedMessageFormat; import org.apache.commons.lang3.text.ExtendedMessageFormat;
@ -33,14 +34,22 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.Logging.CORE;
//TODO, this should be re-evaluated now that ITextComponents are passed everywhere instaed of strings. //TODO, this should be re-evaluated now that ITextComponents are passed everywhere instaed of strings.
public class ForgeI18n { public class ForgeI18n {
private static final Logger LOGGER = LogManager.getLogger(); 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<String,String> i18n; private static Map<String,String> i18n;
private static Map<String,FormatFactory> customFactories; private static Map<String,FormatFactory> customFactories;
// From StringUtils
private static final Pattern PATTERN_CONTROL_CODE = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]");
static { static {
customFactories = new HashMap<>(); customFactories = new HashMap<>();
@ -102,6 +111,16 @@ public class ForgeI18n {
return extendedMessageFormat.format(args); 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 { public static class CustomReadOnlyFormat extends Format {
private final BiConsumer<StringBuffer, Object> formatter; private final BiConsumer<StringBuffer, Object> formatter;

View file

@ -1,154 +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.minecraftforge.eventbus.api.Event;
import net.minecraftforge.forgespi.language.ILifecycleEvent;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public enum LifecycleEventProvider
{
CONSTRUCT(()->new LifecycleEvent(ModLoadingStage.CONSTRUCT)),
CREATE_REGISTRIES(()->new LifecycleEvent(ModLoadingStage.CREATE_REGISTRIES), ModList.inlineDispatcher),
LOAD_REGISTRIES(()->new LifecycleEvent(ModLoadingStage.LOAD_REGISTRIES, LifecycleEvent.Progression.STAY), ModList.inlineDispatcher),
SETUP(()->new LifecycleEvent(ModLoadingStage.COMMON_SETUP)),
SIDED_SETUP(()->new LifecycleEvent(ModLoadingStage.SIDED_SETUP)),
ENQUEUE_IMC(()->new LifecycleEvent(ModLoadingStage.ENQUEUE_IMC)),
PROCESS_IMC(()->new LifecycleEvent(ModLoadingStage.PROCESS_IMC)),
COMPLETE(()->new LifecycleEvent(ModLoadingStage.COMPLETE)),
GATHERDATA(()->new GatherDataLifecycleEvent(ModLoadingStage.GATHERDATA), ModList.inlineDispatcher);
private final Supplier<? extends LifecycleEvent> event;
private final EventHandler<LifecycleEvent, Consumer<List<ModLoadingException>>,Executor, Runnable> eventDispatcher;
private Supplier<Event> customEventSupplier;
private LifecycleEvent.Progression progression = LifecycleEvent.Progression.NEXT;
LifecycleEventProvider(Supplier<? extends LifecycleEvent> e)
{
this(e, ModList.parallelDispatcher);
}
LifecycleEventProvider(Supplier<? extends LifecycleEvent> e, EventHandler<LifecycleEvent, Consumer<List<ModLoadingException>>,Executor, Runnable> eventDispatcher)
{
this.event = e;
this.eventDispatcher = eventDispatcher;
}
public void setCustomEventSupplier(Supplier<Event> eventSupplier) {
this.customEventSupplier = eventSupplier;
}
public void changeProgression(LifecycleEvent.Progression progression) {
this.progression = progression;
}
public void dispatch(Consumer<List<ModLoadingException>> errorHandler, final Executor executor, final Runnable ticker) {
final LifecycleEvent lifecycleEvent = this.event.get();
lifecycleEvent.setCustomEventSupplier(this.customEventSupplier);
lifecycleEvent.changeProgression(this.progression);
this.eventDispatcher.dispatchEvent(lifecycleEvent, errorHandler, executor, ticker);
}
public static class LifecycleEvent implements ILifecycleEvent<LifecycleEvent> {
private final ModLoadingStage stage;
private Supplier<Event> customEventSupplier;
private Progression progression;
LifecycleEvent(final ModLoadingStage stage)
{
this(stage, Progression.NEXT);
}
LifecycleEvent(final ModLoadingStage stage, final Progression progression) {
this.stage = stage;
this.progression = progression;
}
public ModLoadingStage fromStage()
{
return this.stage;
}
public ModLoadingStage toStage()
{
return progression.apply(this.stage);
}
public void setCustomEventSupplier(Supplier<Event> customEventSupplier) {
this.customEventSupplier = customEventSupplier;
}
public void changeProgression(Progression p) {
this.progression = p;
}
public Event getOrBuildEvent(ModContainer modContainer)
{
if (customEventSupplier!=null) return customEventSupplier.get();
return stage.getModEvent(modContainer);
}
@Override
public String toString() {
return "LifecycleEvent:"+stage;
}
public enum Progression {
NEXT((current)-> ModLoadingStage.values()[current.ordinal()+1]),
STAY(Function.identity());
private final Function<ModLoadingStage, ModLoadingStage> edge;
Progression(Function<ModLoadingStage, ModLoadingStage> edge) {
this.edge = edge;
}
public ModLoadingStage apply(ModLoadingStage in) {
return this.edge.apply(in);
}
}
}
private static class GatherDataLifecycleEvent extends LifecycleEvent {
GatherDataLifecycleEvent(final ModLoadingStage stage) {
super(stage);
}
@Override
public ModLoadingStage fromStage() {
return ModLoadingStage.COMMON_SETUP;
}
@Override
public ModLoadingStage toStage() {
return ModLoadingStage.DONE;
}
}
public interface EventHandler<T extends LifecycleEvent, U extends Consumer<? extends List<? super ModLoadingException>>, V extends Executor, R extends Runnable> {
void dispatchEvent(T event, U exceptionHandler, V executor, R ticker);
}
}

View file

@ -21,17 +21,19 @@ package net.minecraftforge.fml;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.IModInfo;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -55,7 +57,7 @@ public abstract class ModContainer
protected final IModInfo modInfo; protected final IModInfo modInfo;
protected ModLoadingStage modLoadingStage; protected ModLoadingStage modLoadingStage;
protected Supplier<?> contextExtension; protected Supplier<?> contextExtension;
protected final Map<ModLoadingStage, Consumer<LifecycleEventProvider.LifecycleEvent>> triggerMap; protected final Map<ModLoadingStage, Runnable> activityMap = new HashMap<>();
protected final Map<ExtensionPoint, Supplier<?>> extensionPoints = new IdentityHashMap<>(); protected final Map<ExtensionPoint, Supplier<?>> extensionPoints = new IdentityHashMap<>();
protected final EnumMap<ModConfig.Type, ModConfig> configs = new EnumMap<>(ModConfig.Type.class); protected final EnumMap<ModConfig.Type, ModConfig> configs = new EnumMap<>(ModConfig.Type.class);
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@ -67,7 +69,6 @@ public abstract class ModContainer
// TODO: Currently not reading namespace from configuration.. // TODO: Currently not reading namespace from configuration..
this.namespace = this.modId; this.namespace = this.modId;
this.modInfo = info; this.modInfo = info;
this.triggerMap = new HashMap<>();
this.modLoadingStage = ModLoadingStage.CONSTRUCT; this.modLoadingStage = ModLoadingStage.CONSTRUCT;
// default displaytest extension checks for version string match // default displaytest extension checks for version string match
registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(()->this.modInfo.getVersion().toString(), registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(()->this.modInfo.getVersion().toString(),
@ -98,28 +99,22 @@ public abstract class ModContainer
return modLoadingStage; return modLoadingStage;
} }
/** public static <T extends Event & IModBusEvent> CompletableFuture<Void> buildTransitionHandler(
* Transition the mod to this event if possible. final ModContainer target,
* @param event to transition to final ModLoadingStage.EventGenerator<T> eventGenerator,
*/ final ModLoadingStage.EventDispatcher<T> eventDispatcher,
public final void transitionState(LifecycleEventProvider.LifecycleEvent event, Consumer<List<ModLoadingException>> errorHandler) final BiFunction<ModLoadingStage, Throwable, ModLoadingStage> stateChangeHandler,
{ final Executor executor) {
if (shutdown) return; //TODO: Remove return CompletableFuture
if (modLoadingStage == event.fromStage()) .runAsync(() -> {
{ ModLoadingContext.get().setActiveContainer(target, target.contextExtension.get());
try target.activityMap.getOrDefault(target.modLoadingStage, ()->{}).run();
{ eventDispatcher.apply(target::acceptEvent).accept(eventGenerator.apply(target));
ModLoadingContext.get().setActiveContainer(this, contextExtension.get()); }, executor)
triggerMap.getOrDefault(modLoadingStage, e->{}).accept(event); .whenComplete((mc, exception) -> {
modLoadingStage = event.toStage(); target.modLoadingStage = stateChangeHandler.apply(target.modLoadingStage, exception);
ModLoadingContext.get().setActiveContainer(null, null); ModLoadingContext.get().setActiveContainer(null, null);
} });
catch (ModLoadingException e)
{
modLoadingStage = ModLoadingStage.ERROR;
errorHandler.accept(Collections.singletonList(e));
}
}
} }
/** /**
@ -165,12 +160,5 @@ public abstract class ModContainer
* Accept an arbitrary event for processing by the mod. Probably posted to an event bus in the lower level container. * Accept an arbitrary event for processing by the mod. Probably posted to an event bus in the lower level container.
* @param e Event to accept * @param e Event to accept
*/ */
protected void acceptEvent(Event e) {} protected <T extends Event & IModBusEvent> void acceptEvent(T e) {}
//TEMPORARY INTERNAL FUNCTION UNTIL net.minecraftforge.fml.ModList.dispatchParallelEvent is fixed.
@Deprecated protected boolean shutdown = false;
@Deprecated protected void shutdown()
{
this.shutdown = true;
}
} }

View file

@ -19,30 +19,37 @@
package net.minecraftforge.fml; package net.minecraftforge.fml;
import net.minecraftforge.fml.loading.FMLConfig; import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
import net.minecraftforge.forgespi.language.ModFileScanData; import net.minecraftforge.forgespi.language.ModFileScanData;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.*; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.LOADING;
/** /**
* Master list of all mods - game-side version. This is classloaded in the game scope and * Master list of all mods - game-side version. This is classloaded in the game scope and
* can dispatch game level events as a result. * can dispatch game level events as a result.
@ -56,7 +63,6 @@ public class ModList
private final Map<String, ModFileInfo> fileById; private final Map<String, ModFileInfo> fileById;
private List<ModContainer> mods; private List<ModContainer> mods;
private Map<String, ModContainer> indexedMods; private Map<String, ModContainer> indexedMods;
private ForkJoinPool modLoadingThreadPool;
private List<ModFileScanData> modFileScanData; private List<ModFileScanData> modFileScanData;
private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList) private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
@ -68,9 +74,6 @@ public class ModList
this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream). this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream).
map(ModInfo.class::cast). map(ModInfo.class::cast).
collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile)); collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile));
final int loadingThreadCount = FMLConfig.loadingThreadCount();
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
modLoadingThreadPool = new ForkJoinPool(loadingThreadCount, ModList::newForkJoinWorkerThread, null, false);
CrashReportExtender.registerCrashCallable("Mod List", this::crashReport); CrashReportExtender.registerCrashCallable("Mod List", this::crashReport);
} }
@ -93,10 +96,6 @@ public class ModList
return INSTANCE; return INSTANCE;
} }
static LifecycleEventProvider.EventHandler<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>, Executor, Runnable> inlineDispatcher = (event, errors, executor, ticker) -> ModList.get().dispatchSynchronousEvent(event, errors, executor, ticker);
static LifecycleEventProvider.EventHandler<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>, Executor, Runnable> parallelDispatcher = (event, errors, executor, ticker) -> ModList.get().dispatchParallelEvent(event, errors, executor, ticker);
public static ModList get() { public static ModList get() {
return INSTANCE; return INSTANCE;
} }
@ -119,39 +118,47 @@ public class ModList
return this.fileById.get(modid); return this.fileById.get(modid);
} }
private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> errorHandler, final Executor executor, final Runnable ticker) { <T extends Event & IModBusEvent> Function<Executor, CompletableFuture<List<Throwable>>> futureVisitor(
LOGGER.debug(LOADING, "Dispatching synchronous event {}", lifecycleEvent); final ModLoadingStage.EventGenerator<T> eventGenerator,
executor.execute(ticker); final ModLoadingStage.EventDispatcher<T> eventManager,
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); final BiFunction<ModLoadingStage, Throwable, ModLoadingStage> stateChange) {
this.mods.stream().forEach(m->m.transitionState(lifecycleEvent, errorHandler)); return executor -> gather(
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); this.mods.stream()
.map(m -> ModContainer.buildTransitionHandler(m, eventGenerator, eventManager, stateChange, executor))
.collect(Collectors.toList()))
.thenComposeAsync(ModList::completableFutureFromExceptionList, executor);
} }
private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> errorHandler, final Executor executor, final Runnable ticker) { static CompletionStage<List<Throwable>> completableFutureFromExceptionList(List<? extends Map.Entry<?, Throwable>> t) {
LOGGER.debug(LOADING, "Dispatching parallel event {}", lifecycleEvent); if (t.stream().noneMatch(e->e.getValue()!=null)) {
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); return CompletableFuture.completedFuture(Collections.emptyList());
DeferredWorkQueue.clear(); } else {
try final List<Throwable> throwables = t.stream().filter(e -> e.getValue() != null).map(Map.Entry::getValue).collect(Collectors.toList());
{ CompletableFuture<List<Throwable>> cf = new CompletableFuture<>();
final ForkJoinTask<?> parallelTask = modLoadingThreadPool.submit(() -> this.mods.parallelStream().forEach(m -> m.transitionState(lifecycleEvent, errorHandler.andThen(e -> this.mods.forEach(ModContainer::shutdown))))); final RuntimeException accumulator = new RuntimeException();
while (ticker != null && !parallelTask.isDone()) { cf.completeExceptionally(accumulator);
executor.execute(ticker); throwables.forEach(exception -> {
} if (exception instanceof CompletionException) {
parallelTask.get(); exception = exception.getCause();
}
if (exception.getSuppressed().length!=0) {
Arrays.stream(exception.getSuppressed()).forEach(accumulator::addSuppressed);
} else {
accumulator.addSuppressed(exception);
}
});
return cf;
} }
catch (InterruptedException | ExecutionException e) }
{
LOGGER.error(LOADING, "Encountered an exception during parallel processing - sleeping 10 seconds to wait for jobs to finish", e); static <V> CompletableFuture<List<Map.Entry<V, Throwable>>> gather(List<? extends CompletableFuture<? extends V>> futures) {
this.mods.forEach(ModContainer::shutdown); //Prevent all future events from being sent out. List<Map.Entry<V, Throwable>> list = new ArrayList<>(futures.size());
errorHandler.accept(Collections.singletonList(new UncaughtModLoadingException(lifecycleEvent.fromStage(), e))); CompletableFuture<?>[] results = new CompletableFuture[futures.size()];
modLoadingThreadPool.awaitQuiescence(10, TimeUnit.SECONDS); futures.forEach(future -> {
if (!modLoadingThreadPool.isQuiescent()) { int i = list.size();
LOGGER.fatal(LOADING, "The parallel pool has failed to quiesce correctly, forcing a shutdown. There is something really wrong here"); list.add(null);
modLoadingThreadPool.shutdownNow(); results[i] = future.whenComplete((result, exception) -> list.set(i, new AbstractMap.SimpleImmutableEntry<>(result, exception)));
throw new RuntimeException("Forge played \"STOP IT NOW MODS!\" - it was \"NOT VERY EFFECTIVE\""); });
} return CompletableFuture.allOf(results).handle((r, th)->null).thenApply(res -> list);
}
DeferredWorkQueue.runTasks(lifecycleEvent.fromStage(), errorHandler);
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
} }
void setLoadedMods(final List<ModContainer> modContainers) void setLoadedMods(final List<ModContainer> modContainers)

View file

@ -29,6 +29,7 @@ import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.config.ConfigTracker; import net.minecraftforge.fml.config.ConfigTracker;
import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent;
import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.FMLPaths; import net.minecraftforge.fml.loading.FMLPaths;
@ -48,12 +49,19 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.Logging.CORE;
@ -104,8 +112,7 @@ public class ModLoader
private final List<ModLoadingException> loadingExceptions; private final List<ModLoadingException> loadingExceptions;
private final List<ModLoadingWarning> loadingWarnings; private final List<ModLoadingWarning> loadingWarnings;
private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig; private boolean loadingStateValid;
private ExistingFileHelper existingFileHelper;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer(); private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
@ -147,40 +154,27 @@ public class ModLoader
return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE; return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE;
} }
public void loadMods(Executor mainThreadExecutor, Consumer<Consumer<Supplier<Event>>> preSidedRunnable, Consumer<Consumer<Supplier<Event>>> postSidedRunnable) { /**
DeferredWorkQueue.workExecutor = mainThreadExecutor; * Run on the primary starting thread by ClientModLoader and ServerModLoader
statusConsumer.ifPresent(c->c.accept("Loading mod config")); * @param syncExecutor An executor to run tasks on the main thread
DistExecutor.runWhenOn(Dist.CLIENT, ()->()-> ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get())); * @param parallelExecutor An executor to run tasks on a parallel loading thread pool
ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get()); * @param periodicTask Optional periodic task to perform on the main thread while other activities run
statusConsumer.ifPresent(c->c.accept("Mod setup: SETUP")); */
dispatchAndHandleError(LifecycleEventProvider.SETUP, mainThreadExecutor, null); public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP")); loadingStateValid = true;
mainThreadExecutor.execute(()->preSidedRunnable.accept(c->ModList.get().forEachModContainer((mi,mc)->mc.acceptEvent(c.get()))));
dispatchAndHandleError(LifecycleEventProvider.SIDED_SETUP, mainThreadExecutor, null);
mainThreadExecutor.execute(()->postSidedRunnable.accept(c->ModList.get().forEachModContainer((mi,mc)->mc.acceptEvent(c.get()))));
statusConsumer.ifPresent(c->c.accept("Mod setup complete"));
}
private static class SpacedRunnable implements Executor {
private static final long FIFTYMILLIS = TimeUnit.MILLISECONDS.toNanos(50);
private long next = System.nanoTime() + FIFTYMILLIS;
@Override
public void execute(final Runnable command) {
final long time = System.nanoTime();
if (next < time) {
command.run();
next = time + FIFTYMILLIS;
}
}
}
public void gatherAndInitializeMods(final Runnable ticker) {
statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete")); statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete"));
FMLLoader.backgroundScanHandler.waitForScanToComplete(ticker); FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask);
statusConsumer.ifPresent(c->c.accept("Loading mods")); statusConsumer.ifPresent(c->c.accept("Loading mods"));
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).collect(Collectors.toList()), loadingModList.getMods()); final ModList modList = ModList.of(loadingModList
.getModFiles()
.stream()
.map(ModFileInfo::getFile)
.collect(Collectors.toList()),
loadingModList.getMods());
if (!this.loadingExceptions.isEmpty()) { if (!this.loadingExceptions.isEmpty()) {
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0)); LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0));
modList.setLoadedMods(Collections.emptyList()); modList.setLoadedMods(Collections.emptyList());
loadingStateValid = false;
throw new LoadingFailedException(loadingExceptions); throw new LoadingFailedException(loadingExceptions);
} }
statusConsumer.ifPresent(c->c.accept("Building Mod List")); statusConsumer.ifPresent(c->c.accept("Building Mod List"));
@ -192,34 +186,86 @@ public class ModLoader
if (!loadingExceptions.isEmpty()) { if (!loadingExceptions.isEmpty()) {
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0)); LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0));
modList.setLoadedMods(Collections.emptyList()); modList.setLoadedMods(Collections.emptyList());
loadingStateValid = false;
throw new LoadingFailedException(loadingExceptions); throw new LoadingFailedException(loadingExceptions);
} }
modList.setLoadedMods(modContainers); modList.setLoadedMods(modContainers);
SpacedRunnable sr = new SpacedRunnable();
statusConsumer.ifPresent(c->c.accept(String.format("Constructing %d mods", modList.size()))); statusConsumer.ifPresent(c->c.accept(String.format("Constructing %d mods", modList.size())));
dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT, sr, ticker); dispatchAndHandleError(ModLoadingStage.CONSTRUCT, syncExecutor, parallelExecutor, periodicTask);
statusConsumer.ifPresent(c->c.accept("Creating registries")); statusConsumer.ifPresent(c->c.accept("Creating registries"));
GameData.fireCreateRegistryEvents(LifecycleEventProvider.CREATE_REGISTRIES, event -> dispatchAndHandleError(event, sr, ticker)); dispatchAndHandleError(ModLoadingStage.CREATE_REGISTRIES, syncExecutor, parallelExecutor, periodicTask);
ObjectHolderRegistry.findObjectHolders(); ObjectHolderRegistry.findObjectHolders();
CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData()); CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData());
statusConsumer.ifPresent(c->c.accept("Populating registries")); statusConsumer.ifPresent(c->c.accept("Populating registries"));
GameData.fireRegistryEvents(rl->true, LifecycleEventProvider.LOAD_REGISTRIES, event -> dispatchAndHandleError(event, sr, ticker)); dispatchAndHandleError(ModLoadingStage.LOAD_REGISTRIES, syncExecutor, parallelExecutor, periodicTask);
statusConsumer.ifPresent(c->c.accept("Early mod loading complete")); statusConsumer.ifPresent(c->c.accept("Early mod loading complete"));
} }
private void dispatchAndHandleError(LifecycleEventProvider event, Executor executor, final Runnable ticker) { public void loadMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Function<Executor, CompletableFuture<Void>> beforeSidedEvent, final Function<Executor, CompletableFuture<Void>> afterSidedEvent, final Runnable periodicTask) {
if (!loadingExceptions.isEmpty()) { statusConsumer.ifPresent(c->c.accept("Loading mod config"));
LOGGER.error(LOADING,"Skipping lifecycle event {}, {} errors found.", event, loadingExceptions.size()); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, ()->()-> ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get()));
} else { ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get());
event.dispatch(this::accumulateErrors, executor, ticker); statusConsumer.ifPresent(c->c.accept("Mod setup: SETUP"));
} dispatchAndHandleError(ModLoadingStage.COMMON_SETUP, syncExecutor, parallelExecutor, periodicTask);
if (!loadingExceptions.isEmpty()) { statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP"));
LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", event, loadingExceptions.size()); dispatchAndHandleError(ModLoadingStage.SIDED_SETUP, syncExecutor, parallelExecutor, periodicTask, beforeSidedEvent, afterSidedEvent);
throw new LoadingFailedException(loadingExceptions); statusConsumer.ifPresent(c->c.accept("Mod setup complete"));
}
} }
private void accumulateErrors(List<ModLoadingException> errors) {
loadingExceptions.addAll(errors); public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask)
{
statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC"));
dispatchAndHandleError(ModLoadingStage.ENQUEUE_IMC, syncExecutor, parallelExecutor, periodicTask);
statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC"));
dispatchAndHandleError(ModLoadingStage.PROCESS_IMC, syncExecutor, parallelExecutor, periodicTask);
statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion"));
dispatchAndHandleError(ModLoadingStage.COMPLETE, syncExecutor, parallelExecutor, periodicTask);
statusConsumer.ifPresent(c->c.accept("Freezing data"));
GameData.freezeData();
NetworkRegistry.lock();
statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", ModList.get().size())));
}
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<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> 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<List<Throwable>> transition) {
while (!transition.isDone()) {
syncExecutor.drive(ticker);
}
try {
transition.join();
} catch (CompletionException e) {
loadingStateValid = false;
Throwable t = e.getCause();
final List<Throwable> notModLoading = Arrays.stream(t.getSuppressed())
.filter(obj -> !(obj instanceof ModLoadingException))
.collect(Collectors.toList());
if (!notModLoading.isEmpty()) {
LOGGER.fatal("Encountered non-modloading exceptions!", e);
throw e;
}
final List<ModLoadingException> modLoadingExceptions = Arrays.stream(t.getSuppressed())
.filter(ModLoadingException.class::isInstance)
.map(ModLoadingException.class::cast)
.collect(Collectors.toList());
LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", state, modLoadingExceptions.size());
throw new LoadingFailedException(modLoadingExceptions);
}
} }
private List<ModContainer> buildMods(final ModFile modFile, final TransformingClassLoader modClassLoader) private List<ModContainer> buildMods(final ModFile modFile, final TransformingClassLoader modClassLoader)
@ -229,7 +275,7 @@ public class ModLoader
LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader()); LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader());
final List<ModContainer> containers = modFile.getScanResult().getTargets().entrySet().stream(). final List<ModContainer> containers = modFile.getScanResult().getTargets().entrySet().stream().
map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e)) map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e))
.filter(e -> e != null) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (containers.size() != modInfoMap.size()) { if (containers.size() != modInfoMap.size()) {
LOGGER.fatal(LOADING,"File {} constructed {} mods: {}, but had {} mods specified: {}", LOGGER.fatal(LOADING,"File {} constructed {} mods: {}, but had {} mods specified: {}",
@ -256,23 +302,28 @@ public class ModLoader
} }
} }
public void postEvent(Event e) { /**
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e)); * @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 finishMods(Executor mainThreadExecutor) public <T extends Event & IModBusEvent> void runEventGenerator(Function<ModContainer, T> generator) {
{ if (!loadingStateValid) {
DeferredWorkQueue.workExecutor = mainThreadExecutor; LOGGER.error("Cowardly refusing to send event generator to a broken mod state");
statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC")); return;
dispatchAndHandleError(LifecycleEventProvider.ENQUEUE_IMC, mainThreadExecutor, null); }
statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC")); ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(generator.apply(mc)));
dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC, mainThreadExecutor, null); }
statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion"));
dispatchAndHandleError(LifecycleEventProvider.COMPLETE, mainThreadExecutor, null); public <T extends Event & IModBusEvent> void postEvent(T e) {
statusConsumer.ifPresent(c->c.accept("Freezing data")); if (!loadingStateValid) {
GameData.freezeData(); LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName());
NetworkRegistry.lock(); return;
statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", ModList.get().size()))); }
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e));
} }
public List<ModLoadingWarning> getWarnings() public List<ModLoadingWarning> getWarnings()
@ -290,20 +341,4 @@ public class ModLoader
public static boolean isDataGenRunning () { public static boolean isDataGenRunning () {
return runningDataGen; return runningDataGen;
} }
public void runDataGenerator(final Set<String> mods, final Path path, final Collection<Path> inputs, Collection<Path> 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<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
return mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper);
}
} }

View file

@ -28,6 +28,7 @@ public class ModLoadingContext
{ {
private static ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new); private static ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new);
private Object languageExtension; private Object languageExtension;
private ModLoadingStage stage;
public static ModLoadingContext get() { public static ModLoadingContext get() {
return context.get(); return context.get();

View file

@ -20,7 +20,9 @@
package net.minecraftforge.fml; package net.minecraftforge.fml;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import net.minecraft.util.StringUtils;
import net.minecraftforge.fml.loading.EarlyLoadingException; import net.minecraftforge.fml.loading.EarlyLoadingException;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.IModInfo;
import java.util.Arrays; import java.util.Arrays;
@ -79,4 +81,11 @@ public class ModLoadingException extends RuntimeException
public String getMessage() { public String getMessage() {
return formatToString(); return formatToString();
} }
public IModInfo getModInfo() {
return modInfo;
}
public String getCleanMessage() {
return ForgeI18n.stripControlCodes(formatToString());
}
} }

View file

@ -19,34 +19,144 @@
package net.minecraftforge.fml; package net.minecraftforge.fml;
import net.minecraftforge.fml.event.lifecycle.*; import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent;
import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent;
import net.minecraftforge.registries.GameData;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream;
public enum ModLoadingStage public enum ModLoadingStage
{ {
ERROR(null), ERROR(),
VALIDATE(null), VALIDATE(),
CONSTRUCT(null), CONSTRUCT(FMLConstructModEvent.class),
CREATE_REGISTRIES(null), CREATE_REGISTRIES(()->Stream.of(RegistryEvent.NewRegistry::new), EventDispatcher.identity()),
LOAD_REGISTRIES(null), LOAD_REGISTRIES(GameData::generateRegistryEvents, GameData.buildRegistryEventDispatch()),
COMMON_SETUP(()-> FMLCommonSetupEvent::new), COMMON_SETUP(FMLCommonSetupEvent.class),
SIDED_SETUP(SidedProvider.SIDED_SETUP_EVENT::get), SIDED_SETUP(DistExecutor.unsafeRunForDist(()->()->FMLClientSetupEvent.class, ()->()->FMLDedicatedServerSetupEvent.class)),
ENQUEUE_IMC(()-> InterModEnqueueEvent::new), ENQUEUE_IMC(InterModEnqueueEvent.class),
PROCESS_IMC(()-> InterModProcessEvent::new), PROCESS_IMC(InterModProcessEvent.class),
COMPLETE(()-> FMLLoadCompleteEvent::new), COMPLETE(FMLLoadCompleteEvent.class),
DONE(null), DONE();
GATHERDATA(ModLoader.get()::getDataGeneratorEvent);
private final Supplier<Function<ModContainer, ModLifecycleEvent>> modLifecycleEventFunction;
ModLoadingStage(Supplier<Function<ModContainer, ModLifecycleEvent>> modLifecycleEventFunction) private final Supplier<Stream<EventGenerator<?>>> eventFunctionStream;
{ private final EventDispatcher<?> eventManager;
this.modLifecycleEventFunction = modLifecycleEventFunction; private final Optional<Class<? extends ParallelDispatchEvent>> parallelEventClass;
private final ThreadSelector threadSelector;
private final BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator;
private DeferredWorkQueue deferredWorkQueue;
ModLoadingStage(Class<? extends ParallelDispatchEvent> parallelClass) {
final EventGenerator<?> event = EventGenerator.fromFunction(LamdbaExceptionUtils.rethrowFunction((ModContainer mc) -> parallelClass.getConstructor(ModContainer.class).newInstance(mc)));
this.eventFunctionStream = ()->Stream.of(event);
this.threadSelector = ThreadSelector.PARALLEL;
this.eventManager = EventDispatcher.identity();
this.parallelEventClass = Optional.of(parallelClass);
deferredWorkQueue = new DeferredWorkQueue(this, parallelClass);
this.finalActivityGenerator = (e, prev) -> prev.thenApplyAsync((List<Throwable> t) -> {
deferredWorkQueue.runTasks();
return t;
}, e);
} }
public ModLifecycleEvent getModEvent(ModContainer modContainer) @SuppressWarnings("unchecked")
{ <T extends Event & IModBusEvent> ModLoadingStage(Supplier<Stream<EventGenerator<?>>> eventStream, EventDispatcher<?> eventManager) {
return modLifecycleEventFunction.get().apply(modContainer); this.eventFunctionStream = eventStream;
this.parallelEventClass = Optional.empty();
this.eventManager = eventManager;
this.threadSelector = ThreadSelector.SYNC;
this.finalActivityGenerator = (e, prev) ->prev.thenApplyAsync(Function.identity(), e);
}
ModLoadingStage() {
this(ParallelDispatchEvent.class);
}
public <T extends Event & IModBusEvent> CompletableFuture<List<Throwable>> buildTransition(final Executor syncExecutor, final Executor parallelExecutor) {
return buildTransition(syncExecutor, parallelExecutor, e->CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e));
}
@SuppressWarnings("unchecked")
public <T extends Event & IModBusEvent> CompletableFuture<List<Throwable>> buildTransition(final Executor syncExecutor, final Executor parallelExecutor, Function<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> postSyncTask) {
List<CompletableFuture<List<Throwable>>> cfs = new ArrayList<>();
EventDispatcher<T> em = (EventDispatcher<T>) this.eventManager;
eventFunctionStream.get()
.map(f->(EventGenerator<T>)f)
.reduce((head, tail)->{
cfs.add(ModList.get()
.futureVisitor(head, em, ModLoadingStage::currentState)
.apply(threadSelector.apply(syncExecutor, parallelExecutor))
);
return tail;
})
.ifPresent(last->cfs.add(ModList.get().futureVisitor(last, em, ModLoadingStage::nextState).apply(threadSelector.apply(syncExecutor, parallelExecutor))));
final CompletableFuture<Void> preSyncTaskCF = preSyncTask.apply(syncExecutor);
final CompletableFuture<List<Throwable>> eventDispatchCF = ModList.gather(cfs).thenCompose(ModList::completableFutureFromExceptionList);
final CompletableFuture<List<Throwable>> postEventDispatchCF = preSyncTaskCF.thenComposeAsync(n -> eventDispatchCF, parallelExecutor).thenApply(r -> {
postSyncTask.apply(syncExecutor);
return r;
});
return this.finalActivityGenerator.apply(syncExecutor, postEventDispatchCF);
}
ModLoadingStage nextState(Throwable exception) {
return exception != null ? ERROR : values()[this.ordinal()+1];
}
ModLoadingStage currentState(Throwable exception) {
return exception != null ? ERROR : this;
}
public Optional<Class<? extends ParallelDispatchEvent>> getParallelEventClass() {
return parallelEventClass;
}
enum ThreadSelector implements BinaryOperator<Executor> {
SYNC((sync, parallel)->sync),
PARALLEL((sync, parallel)->parallel);
private final BinaryOperator<Executor> selector;
ThreadSelector(final BinaryOperator<Executor> selector) {
this.selector = selector;
}
@Override
public Executor apply(final Executor sync, final Executor parallel) {
return this.selector.apply(sync, parallel);
}
}
public DeferredWorkQueue getDeferredWorkQueue() {
return deferredWorkQueue;
}
public interface EventGenerator<T extends Event & IModBusEvent> extends Function<ModContainer, T> {
static <FN extends Event & IModBusEvent> EventGenerator<FN> fromFunction(Function<ModContainer, FN> fn) {
return fn::apply;
}
}
public interface EventDispatcher<T extends Event & IModBusEvent> extends Function<Consumer<? super T>, Consumer<? super T>> {
static <FN extends Event & IModBusEvent> EventDispatcher<FN> identity() {
return consumer -> consumer;
}
} }
} }

View file

@ -0,0 +1,103 @@
package net.minecraftforge.fml;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import static net.minecraftforge.fml.Logging.LOADING;
public class ModWorkManager {
private static final Logger LOGGER = LogManager.getLogger();
public interface DrivenExecutor extends Executor {
boolean selfDriven();
boolean driveOne();
default void drive(Runnable ticker) {
if (!selfDriven()) {
while (driveOne())
ticker.run();
}
}
}
private static class SyncExecutor implements DrivenExecutor {
private ConcurrentLinkedDeque<Runnable> tasks = new ConcurrentLinkedDeque<>();
@Override
public boolean driveOne() {
final Runnable task = tasks.pollFirst();
if (task != null) {
task.run();
}
return task != null;
}
@Override
public boolean selfDriven() {
return false;
}
@Override
public void execute(final Runnable command) {
tasks.addLast(command);
}
}
private static class WrappingExecutor implements DrivenExecutor {
private final Executor wrapped;
public WrappingExecutor(final Executor executor) {
this.wrapped = executor;
}
@Override
public boolean selfDriven() {
return true;
}
@Override
public boolean driveOne() {
return false;
}
@Override
public void execute(final Runnable command) {
wrapped.execute(command);
}
}
private static SyncExecutor syncExecutor;
public static DrivenExecutor syncExecutor() {
if (syncExecutor == null)
syncExecutor = new SyncExecutor();
return syncExecutor;
}
public static DrivenExecutor wrappedExecutor(Executor executor) {
return new WrappingExecutor(executor);
}
private static ForkJoinPool parallelThreadPool;
public static Executor parallelExecutor() {
if (parallelThreadPool == null) {
final int loadingThreadCount = 2;
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
parallelThreadPool = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false);
}
return parallelThreadPool;
}
private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) {
ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
thread.setName("modloading-worker-" + thread.getPoolIndex());
// The default sets it to the SystemClassloader, so copy the current one.
thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
return thread;
}
}

View file

@ -1,85 +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.eventbus.api.Event;
import net.minecraftforge.fml.client.ClientHooks;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
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(); }),
SIDED_SETUP_EVENT(
(Function<Supplier<Minecraft>, Function<ModContainer, Event>>)c-> mc->new FMLClientSetupEvent(c, mc),
s-> mc->new FMLDedicatedServerSetupEvent(mc),
()-> { throw new UnsupportedOperationException(); }),
STRIPCHARS(
(Function<Supplier<Minecraft>, Function<String, String>>)c-> ClientHooks::stripSpecialChars,
s-> str->str,
()-> str->str);
private static Supplier<Minecraft> client;
private static Supplier<DedicatedServer> server;
public static void setClient(Supplier<Minecraft> client)
{
SidedProvider.client = client;
}
public static void setServer(Supplier<DedicatedServer> server)
{
SidedProvider.server = server;
}
private final Function<Supplier<Minecraft>, ?> clientSide;
private final Function<Supplier<DedicatedServer>, ?> serverSide;
private final Supplier<?> testSide;
<T> SidedProvider(Function<Supplier<Minecraft>, T> clientSide, Function<Supplier<DedicatedServer>, T> serverSide, Supplier<T> testSide)
{
this.clientSide = clientSide;
this.serverSide = serverSide;
this.testSide = testSide;
}
@SuppressWarnings("unchecked")
public <T> 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();
}
}
}

View file

@ -75,9 +75,6 @@ public class ClientHooks
{ {
private static final Logger LOGGER = LogManager.getLogger(); private static final Logger LOGGER = LogManager.getLogger();
private static final Marker CLIENTHOOKS = MarkerManager.getMarker("CLIENTHOOKS"); 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"); private static final ResourceLocation iconSheet = new ResourceLocation(ForgeVersion.MOD_ID, "textures/gui/icons.png");
@Nullable @Nullable
@ -209,12 +206,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<String,ResourceLocation> missingTextures = HashMultimap.create(); private static SetMultimap<String,ResourceLocation> missingTextures = HashMultimap.create();
private static Set<String> badTextureDomains = Sets.newHashSet(); private static Set<String> badTextureDomains = Sets.newHashSet();
private static Table<String, String, Set<ResourceLocation>> brokenTextures = HashBasedTable.create(); private static Table<String, String, Set<ResourceLocation>> brokenTextures = HashBasedTable.create();

View file

@ -22,19 +22,40 @@ package net.minecraftforge.fml.client;
import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING; import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import net.minecraft.resources.*; 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.VersionChecker;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -50,16 +71,6 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.ForgeConfig; import net.minecraftforge.common.ForgeConfig;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.Event;
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.SidedProvider;
import net.minecraftforge.fml.VersionChecker;
import net.minecraftforge.fml.client.gui.screen.LoadingErrorScreen; import net.minecraftforge.fml.client.gui.screen.LoadingErrorScreen;
import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFile;
@ -75,66 +86,89 @@ public class ClientModLoader
private static final Logger LOGGER = LogManager.getLogger(); private static final Logger LOGGER = LogManager.getLogger();
private static boolean loading; private static boolean loading;
private static Minecraft mc; private static Minecraft mc;
private static boolean loadingComplete;
private static LoadingFailedException error; private static LoadingFailedException error;
private static EarlyLoaderGUI earlyLoaderGUI; 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) 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 // 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)); Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown));
loading = true; loading = true;
ClientModLoader.mc = minecraft; ClientModLoader.mc = minecraft;
SidedProvider.setClient(()->minecraft);
LogicalSidedProvider.setClient(()->minecraft); LogicalSidedProvider.setClient(()->minecraft);
LanguageHook.loadForgeAndMCLangs(); LanguageHook.loadForgeAndMCLangs();
earlyLoaderGUI = new EarlyLoaderGUI(minecraft.getMainWindow()); earlyLoaderGUI = new EarlyLoaderGUI(minecraft.getMainWindow());
createRunnableWithCatch(() -> ModLoader.get().gatherAndInitializeMods(earlyLoaderGUI::renderTick)).run(); createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), new SpacedRunnable(earlyLoaderGUI::renderTick))).run();
ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder); if (error == null) {
mcResourceManager.addReloadListener(ClientModLoader::onreload); ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder);
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener()); DatapackCodec.field_234880_a_.addModPacks(ResourcePackLoader.getPackNames());
ModelLoaderRegistry.init(); mcResourceManager.addReloadListener(ClientModLoader::onResourceReload);
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener());
ModelLoaderRegistry.init();
}
} }
private static CompletableFuture<Void> onreload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) { private static CompletableFuture<Void> onResourceReload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(syncExecutor)), asyncExecutor). return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor)), asyncExecutor)
thenCompose(stage::markCompleteAwaitingOthers). .thenCompose(stage::markCompleteAwaitingOthers)
thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor); .thenRunAsync(() -> finishModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor), asyncExecutor);
} }
private static Runnable createRunnableWithCatch(Runnable r) { private static Runnable createRunnableWithCatch(Runnable r) {
return ()-> { return ()-> {
if (loadingComplete) return;
try { try {
r.run(); r.run();
} catch (LoadingFailedException e) { } catch (LoadingFailedException e) {
MinecraftForge.EVENT_BUS.shutdown();
if (error == null) error = e; if (error == null) error = e;
} }
}; };
} }
private static void startModLoading(Executor executor) { private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) {
earlyLoaderGUI.handleElsewhere(); earlyLoaderGUI.handleElsewhere();
createRunnableWithCatch(() -> ModLoader.get().loadMods(executor, ClientModLoader::preSidedRunnable, ClientModLoader::postSidedRunnable)).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(Consumer<Supplier<Event>> perModContainerEventProcessor) { private static void postSidedRunnable() {
LOGGER.debug(LOADING, "Running post client event work");
RenderingRegistry.loadEntityRenderers(mc.getRenderManager()); RenderingRegistry.loadEntityRenderers(mc.getRenderManager());
} }
private static void preSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) { private static void preSidedRunnable() {
LOGGER.debug(LOADING, "Running pre client event work");
} }
private static void finishModLoading(Executor executor) private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor)
{ {
createRunnableWithCatch(() -> ModLoader.get().finishMods(executor)).run(); createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, new SpacedRunnable(earlyLoaderGUI::renderTick))).run();
loading = false; loading = false;
loadingComplete = true;
// reload game settings on main thread // reload game settings on main thread
executor.execute(()->mc.gameSettings.loadOptions()); syncExecutor.execute(()->mc.gameSettings.loadOptions());
} }
public static VersionChecker.Status checkForUpdates() public static VersionChecker.Status checkForUpdates()
{ {
boolean anyOutdated = ModList.get().getMods().stream() boolean anyOutdated = ModList.get().getMods().stream()
.map(VersionChecker::getResult) .map(VersionChecker::getResult)
.map(result -> result.status) .map(result -> result.status)
@ -164,6 +198,24 @@ public class ClientModLoader
if (error == null) { if (error == null) {
// We can finally start the forge eventbus up // We can finally start the forge eventbus up
MinecraftForge.EVENT_BUS.start(); 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()) { if (error != null || !warnings.isEmpty()) {
mc.displayGuiScreen(new LoadingErrorScreen(error, warnings)); mc.displayGuiScreen(new LoadingErrorScreen(error, warnings));

View file

@ -38,14 +38,14 @@ import java.util.function.Supplier;
* *
* This is a parallel dispatch event. * This is a parallel dispatch event.
*/ */
public class FMLClientSetupEvent extends ModLifecycleEvent public class FMLClientSetupEvent extends ParallelDispatchEvent
{ {
private final Supplier<Minecraft> minecraftSupplier; private final Supplier<Minecraft> minecraftSupplier;
public FMLClientSetupEvent(Supplier<Minecraft> mc, ModContainer container) public FMLClientSetupEvent(ModContainer container)
{ {
super(container); super(container);
this.minecraftSupplier = mc; minecraftSupplier = Minecraft::getInstance;
} }
public Supplier<Minecraft> getMinecraftSupplier() public Supplier<Minecraft> getMinecraftSupplier()

View file

@ -40,7 +40,7 @@ import java.util.function.Consumer;
* @see net.minecraftforge.fml.DeferredWorkQueue to enqueue work to run on the main game thread after this event has * @see net.minecraftforge.fml.DeferredWorkQueue to enqueue work to run on the main game thread after this event has
* completed dispatch * completed dispatch
*/ */
public class FMLCommonSetupEvent extends ModLifecycleEvent public class FMLCommonSetupEvent extends ParallelDispatchEvent
{ {
public FMLCommonSetupEvent(final ModContainer container) public FMLCommonSetupEvent(final ModContainer container)
{ {

View file

@ -0,0 +1,9 @@
package net.minecraftforge.fml.event.lifecycle;
import net.minecraftforge.fml.ModContainer;
public class FMLConstructModEvent extends ParallelDispatchEvent {
public FMLConstructModEvent(final ModContainer container) {
super(container);
}
}

View file

@ -42,7 +42,7 @@ import java.util.function.Supplier;
* *
* This is a parallel dispatch event. * This is a parallel dispatch event.
*/ */
public class FMLDedicatedServerSetupEvent extends ModLifecycleEvent public class FMLDedicatedServerSetupEvent extends ParallelDispatchEvent
{ {
public FMLDedicatedServerSetupEvent(ModContainer container) public FMLDedicatedServerSetupEvent(ModContainer container)
{ {

View file

@ -27,7 +27,7 @@ import net.minecraftforge.fml.ModContainer;
* *
* @author cpw * @author cpw
*/ */
public class FMLLoadCompleteEvent extends ModLifecycleEvent public class FMLLoadCompleteEvent extends ParallelDispatchEvent
{ {
public FMLLoadCompleteEvent(final ModContainer container) public FMLLoadCompleteEvent(final ModContainer container)
{ {

View file

@ -22,6 +22,7 @@ package net.minecraftforge.fml.event.lifecycle;
import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraftforge.client.model.generators.ExistingFileHelper; import net.minecraftforge.client.model.generators.ExistingFileHelper;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModContainer;
import java.nio.file.Path; import java.nio.file.Path;
@ -33,19 +34,25 @@ import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class GatherDataEvent extends ModLifecycleEvent public class GatherDataEvent extends Event implements IModBusEvent
{ {
private final DataGenerator dataGenerator; private final DataGenerator dataGenerator;
private final DataGeneratorConfig config; private final DataGeneratorConfig config;
private final ExistingFileHelper existingFileHelper; 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.dataGenerator = dataGenerator;
this.config = dataGeneratorConfig; this.config = dataGeneratorConfig;
this.existingFileHelper = existingFileHelper; this.existingFileHelper = existingFileHelper;
} }
public ModContainer getModContainer() {
return this.modContainer;
}
public DataGenerator getGenerator() { return this.dataGenerator; } public DataGenerator getGenerator() { return this.dataGenerator; }
public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; } public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; }
public boolean includeServer() { return this.config.server; } public boolean includeServer() { return this.config.server; }

View file

@ -32,7 +32,7 @@ import net.minecraftforge.fml.ModContainer;
* *
* This is a parallel dispatch event. * This is a parallel dispatch event.
*/ */
public class InterModEnqueueEvent extends ModLifecycleEvent public class InterModEnqueueEvent extends ParallelDispatchEvent
{ {
public InterModEnqueueEvent(final ModContainer container) public InterModEnqueueEvent(final ModContainer container)

View file

@ -36,7 +36,7 @@ import java.util.function.Predicate;
* @see #getIMCStream() * @see #getIMCStream()
* @see #getIMCStream(Predicate) * @see #getIMCStream(Predicate)
*/ */
public class InterModProcessEvent extends ModLifecycleEvent public class InterModProcessEvent extends ParallelDispatchEvent
{ {
public InterModProcessEvent(final ModContainer container) public InterModProcessEvent(final ModContainer container)
{ {

View file

@ -54,6 +54,10 @@ public class ModLifecycleEvent extends Event implements IModBusEvent
return InterModComms.getMessages(this.container.getModId(), methodFilter); return InterModComms.getMessages(this.container.getModId(), methodFilter);
} }
ModContainer getContainer() {
return this.container;
}
@Override @Override
public String toString() { public String toString() {
return description(); return description();

View file

@ -0,0 +1,26 @@
package net.minecraftforge.fml.event.lifecycle;
import net.minecraftforge.fml.DeferredWorkQueue;
import net.minecraftforge.fml.ModContainer;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public class ParallelDispatchEvent extends ModLifecycleEvent {
public ParallelDispatchEvent(final ModContainer container) {
super(container);
}
private Optional<DeferredWorkQueue> getQueue() {
return DeferredWorkQueue.lookup(Optional.of(getClass()));
}
public CompletableFuture<Void> enqueueWork(Runnable work) {
return getQueue().map(q->q.enqueueWork(getContainer().getModInfo(), work)).orElseThrow(()->new RuntimeException("No work queue found!"));
}
public <T> CompletableFuture<T> enqueueWork(Supplier<T> work) {
return getQueue().map(q->q.enqueueWork(getContainer().getModInfo(), work)).orElseThrow(()->new RuntimeException("No work queue found!"));
}
}

View file

@ -25,22 +25,16 @@ import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.IEventListener; import net.minecraftforge.eventbus.api.IEventListener;
import net.minecraftforge.fml.AutomaticEventSubscriber; import net.minecraftforge.fml.AutomaticEventSubscriber;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingException; import net.minecraftforge.fml.ModLoadingException;
import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.event.lifecycle.IModBusEvent; import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.ModFileScanData; import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.LOADING;
@ -57,17 +51,9 @@ public class FMLModContainer extends ModContainer
super(info); super(info);
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {} with classLoader {} & {}", className, modClassLoader, getClass().getClassLoader()); LOGGER.debug(LOADING,"Creating FMLModContainer instance for {} with classLoader {} & {}", className, modClassLoader, getClass().getClassLoader());
this.scanResults = modFileScanResults; this.scanResults = modFileScanResults;
triggerMap.put(ModLoadingStage.CONSTRUCT, dummy().andThen(this::beforeEvent).andThen(this::constructMod).andThen(this::afterEvent)); activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
triggerMap.put(ModLoadingStage.CREATE_REGISTRIES, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.LOAD_REGISTRIES, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.COMMON_SETUP, dummy().andThen(this::beforeEvent).andThen(this::preinitMod).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.SIDED_SETUP, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.ENQUEUE_IMC, dummy().andThen(this::beforeEvent).andThen(this::initMod).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.PROCESS_IMC, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.COMPLETE, dummy().andThen(this::beforeEvent).andThen(this::completeLoading).andThen(this::fireEvent).andThen(this::afterEvent));
triggerMap.put(ModLoadingStage.GATHERDATA, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent));
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).build(); this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).build();
this.configHandler = Optional.of(event -> this.eventBus.post(event)); this.configHandler = Optional.of(this.eventBus::post);
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this); final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
this.contextExtension = () -> contextExtension; this.contextExtension = () -> contextExtension;
try try
@ -82,53 +68,12 @@ public class FMLModContainer extends ModContainer
} }
} }
private void completeLoading(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
private void initMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
private Consumer<LifecycleEventProvider.LifecycleEvent> dummy() { return new ErroringConsumer<>(); }
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable)
{ {
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable)); LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
} }
private void beforeEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { private void constructMod()
}
private void fireEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) {
final Event event = lifecycleEvent.getOrBuildEvent(this);
LOGGER.debug(LOADING, "Firing event for modid {} : {}", this.getModId(), event);
try
{
eventBus.post(event);
LOGGER.debug(LOADING, "Fired event for modid {} : {}", this.getModId(), event);
}
catch (Throwable e)
{
LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", event, this.getModId(), e);
throw new ModLoadingException(modInfo, lifecycleEvent.fromStage(), "fml.modloading.errorduringevent", e);
}
}
private void afterEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) {
if (getCurrentState() == ModLoadingStage.ERROR) {
LOGGER.error(LOADING,"An error occurred while dispatching event {} to {}", lifecycleEvent.fromStage(), getModId());
this.eventBus.shutdown();
}
}
private void preinitMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
private void constructMod(LifecycleEventProvider.LifecycleEvent event)
{ {
try try
{ {
@ -139,7 +84,7 @@ public class FMLModContainer extends ModContainer
catch (Throwable e) catch (Throwable e)
{ {
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e); LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, event.fromStage(), "fml.modloading.failedtoloadmod", e, modClass); throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
} }
try { try {
LOGGER.debug(LOADING, "Injecting Automatic event subscribers for {}", getModId()); LOGGER.debug(LOADING, "Injecting Automatic event subscribers for {}", getModId());
@ -147,7 +92,7 @@ public class FMLModContainer extends ModContainer
LOGGER.debug(LOADING, "Completed Automatic event subscribers for {}", getModId()); LOGGER.debug(LOADING, "Completed Automatic event subscribers for {}", getModId());
} catch (Throwable e) { } catch (Throwable e) {
LOGGER.error(LOADING,"Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e); LOGGER.error(LOADING,"Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, event.fromStage(), "fml.modloading.failedtoloadmod", e, modClass); throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
} }
} }
@ -169,47 +114,14 @@ public class FMLModContainer extends ModContainer
} }
@Override @Override
protected void acceptEvent(final Event e) protected <T extends Event & IModBusEvent> void acceptEvent(final T e) {
{ try {
if (this.shutdown) return; LOGGER.debug(LOADING, "Firing event for modid {} : {}", this.getModId(), e);
this.eventBus.post(e); this.eventBus.post(e);
} LOGGER.debug(LOADING, "Fired event for modid {} : {}", this.getModId(), e);
} catch (Throwable t) {
private class ErroringConsumer<T> implements Consumer<T> LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", e, this.getModId(), t);
{ throw new ModLoadingException(modInfo, modLoadingStage, "fml.modloading.errorduringevent", t);
private List<Consumer<? super T>> children = new ArrayList<>();
@Override
public void accept(T t)
{
Throwable error = null;
for (Consumer<? super T> child : children)
{
try
{
child.accept(t);
}
catch (Throwable e)
{
FMLModContainer.this.modLoadingStage = ModLoadingStage.ERROR;
error = e;
}
}
if (error != null)
{
if (error instanceof RuntimeException)
throw (RuntimeException)error;
throw new RuntimeException(error);
}
}
@Override
public ErroringConsumer<T> andThen(Consumer<? super T> after)
{
Objects.requireNonNull(after);
children.add(after);
return this;
} }
} }
} }

View file

@ -55,16 +55,16 @@ public class ResourcePackLoader
public static void loadResourcePacks(ResourcePackList resourcePacks, BiFunction<Map<ModFile, ? extends ModFileResourcePack>, BiConsumer<? super ModFileResourcePack, ResourcePackInfo>, IPackInfoFinder> packFinder) { public static void loadResourcePacks(ResourcePackList resourcePacks, BiFunction<Map<ModFile, ? extends ModFileResourcePack>, BiConsumer<? super ModFileResourcePack, ResourcePackInfo>, IPackInfoFinder> packFinder) {
resourcePackList = resourcePacks; resourcePackList = resourcePacks;
modResourcePacks = ModList.get().getModFiles().stream(). modResourcePacks = ModList.get().getModFiles().stream()
filter(mf->!Objects.equals(mf.getModLoader(),"minecraft")). .filter(mf->!Objects.equals(mf.getModLoader(),"minecraft"))
map(mf -> new ModFileResourcePack(mf.getFile())). .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)); .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))); resourcePacks.addPackFinder(new LambdaFriendlyPackFinder(packFinder.apply(modResourcePacks, ModFileResourcePack::setPackInfo)));
} }
public static List<String> getPackNames() public static List<String> 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 <V> Comparator<Map.Entry<String,V>> getSorter() { public static <V> Comparator<Map.Entry<String,V>> getSorter() {

View file

@ -31,14 +31,12 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.minecraft.resources.IPackNameDecorator; import net.minecraft.resources.IPackNameDecorator;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.storage.FolderName; import net.minecraft.world.storage.FolderName;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraftforge.fml.LogicalSidedProvider;
import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.ModLoadingWarning; import net.minecraftforge.fml.ModLoadingWarning;
import net.minecraftforge.fml.SidedProvider;
import net.minecraftforge.fml.network.ConnectionType; import net.minecraftforge.fml.network.ConnectionType;
import net.minecraftforge.fml.network.FMLNetworkConstants; import net.minecraftforge.fml.network.FMLNetworkConstants;
import net.minecraftforge.fml.network.FMLStatusPing; import net.minecraftforge.fml.network.FMLStatusPing;
@ -92,7 +90,6 @@ public class ServerLifecycleHooks
currentServer = server; currentServer = server;
currentServer.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data currentServer.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data
// on the dedi server we need to force the stuff to setup properly // 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); LogicalSidedProvider.setServer(()->server);
ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server)); ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server));
return !MinecraftForge.EVENT_BUS.post(new FMLServerAboutToStartEvent(server)); return !MinecraftForge.EVENT_BUS.post(new FMLServerAboutToStartEvent(server));
@ -185,30 +182,6 @@ public class ServerLifecycleHooks
public static void handleExit(int retVal) 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); System.exit(retVal);
} }
@ -235,5 +208,4 @@ public class ServerLifecycleHooks
consumer.accept(packInfo); consumer.accept(packInfo);
} }
} }
} }

View file

@ -19,16 +19,23 @@
package net.minecraftforge.fml.server; package net.minecraftforge.fml.server;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.LoadingFailedException; import net.minecraftforge.fml.LoadingFailedException;
import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraftforge.fml.LogicalSidedProvider;
import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModLoadingWarning; import net.minecraftforge.fml.ModLoadingWarning;
import net.minecraftforge.fml.SidedProvider; import net.minecraftforge.fml.ModWorkManager;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 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.List;
import java.util.concurrent.CompletableFuture;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING; import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
@ -38,20 +45,34 @@ public class ServerModLoader
private static boolean hasErrors = false; private static boolean hasErrors = false;
public static void load() { public static void load() {
SidedProvider.setServer(()-> {
throw new IllegalStateException("Unable to access server yet");
});
LogicalSidedProvider.setServer(()-> { LogicalSidedProvider.setServer(()-> {
throw new IllegalStateException("Unable to access server yet"); throw new IllegalStateException("Unable to access server yet");
}); });
LanguageHook.loadForgeAndMCLangs(); LanguageHook.loadForgeAndMCLangs();
try { try {
ModLoader.get().gatherAndInitializeMods(() -> {}); ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
ModLoader.get().loadMods(Runnable::run, (a)->{}, (a)->{}); ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), e-> CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e), ()->{});
ModLoader.get().finishMods(Runnable::run); ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
} catch (LoadingFailedException e) { } catch (LoadingFailedException error) {
ServerModLoader.hasErrors = true; 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<ModLoadingWarning> warnings = ModLoader.get().getWarnings(); List<ModLoadingWarning> warnings = ModLoader.get().getWarnings();
if (!warnings.isEmpty()) { if (!warnings.isEmpty()) {

View file

@ -53,7 +53,6 @@ import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.registry.DefaultedRegistry; import net.minecraft.util.registry.DefaultedRegistry;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.SimpleRegistry; import net.minecraft.util.registry.SimpleRegistry;
import net.minecraft.village.PointOfInterestType; import net.minecraft.village.PointOfInterestType;
import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biome;
@ -72,8 +71,8 @@ import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.loot.GlobalLootModifierSerializer; import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.RegistryEvent.MissingMappings; import net.minecraftforge.event.RegistryEvent.MissingMappings;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.common.EnhancedRuntimeException; import net.minecraftforge.fml.common.EnhancedRuntimeException;
import net.minecraftforge.fml.common.thread.EffectiveSide; import net.minecraftforge.fml.common.thread.EffectiveSide;
import net.minecraftforge.fml.event.lifecycle.FMLModIdMappingEvent; import net.minecraftforge.fml.event.lifecycle.FMLModIdMappingEvent;
@ -94,9 +93,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES; import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES;
import static net.minecraftforge.registries.ForgeRegistries.Keys.*; import static net.minecraftforge.registries.ForgeRegistries.Keys.*;
@ -328,6 +326,34 @@ public class GameData
LOGGER.debug(REGISTRIES, "Reverting complete"); LOGGER.debug(REGISTRIES, "Reverting complete");
} }
public static Stream<ModLoadingStage.EventGenerator<?>> generateRegistryEvents() {
List<ResourceLocation> keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet());
keys.sort((o1, o2) -> String.valueOf(o1).compareToIgnoreCase(String.valueOf(o2)));
//Move Blocks to first, and Items to second.
keys.remove(BLOCKS);
keys.remove(ITEMS);
keys.add(0, BLOCKS);
keys.add(1, ITEMS);
return keys.stream().map(rl -> mc -> RegistryManager.ACTIVE.getRegistry(rl).getRegisterEvent(rl));
}
public static ModLoadingStage.EventDispatcher<RegistryEvent.Register<?>> buildRegistryEventDispatch() {
return eventConsumer -> eventToSend -> {
final ResourceLocation rl = eventToSend.getName();
ForgeRegistry<?> fr = (ForgeRegistry<?>) eventToSend.getRegistry();
StartupMessageManager.modLoaderConsumer().ifPresent(s->s.accept("REGISTERING "+rl));
fr.unfreeze();
eventConsumer.accept(eventToSend);
fr.freeze();
LOGGER.debug(REGISTRIES,"Applying holder lookups: {}", rl.toString());
ObjectHolderRegistry.applyObjectHolders(rl::equals);
LOGGER.debug(REGISTRIES,"Holder lookups applied: {}", rl.toString());
};
}
//Lets us clear the map so we can rebuild it. //Lets us clear the map so we can rebuild it.
private static class ClearableObjectIntIdentityMap<I> extends ObjectIntIdentityMap<I> private static class ClearableObjectIntIdentityMap<I> extends ObjectIntIdentityMap<I>
{ {
@ -794,47 +820,6 @@ public class GameData
newRegistry.loadIds(_new, frozen.getOverrideOwners(), Maps.newLinkedHashMap(), remaps, frozen, name); newRegistry.loadIds(_new, frozen.getOverrideOwners(), Maps.newLinkedHashMap(), remaps, frozen, name);
} }
public static void fireCreateRegistryEvents()
{
MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry());
}
public static void fireCreateRegistryEvents(final LifecycleEventProvider lifecycleEventProvider, final Consumer<LifecycleEventProvider> eventDispatcher) {
final RegistryEvent.NewRegistry newRegistryEvent = new RegistryEvent.NewRegistry();
lifecycleEventProvider.setCustomEventSupplier(()->newRegistryEvent);
eventDispatcher.accept(lifecycleEventProvider);
}
public static void fireRegistryEvents(Predicate<ResourceLocation> filter, final LifecycleEventProvider lifecycleEventProvider, final Consumer<LifecycleEventProvider> eventDispatcher)
{
List<ResourceLocation> keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet());
keys.sort((o1, o2) -> String.valueOf(o1).compareToIgnoreCase(String.valueOf(o2)));
//Move Blocks to first, and Items to second.
keys.remove(BLOCKS.func_240901_a_());
keys.remove(ITEMS.func_240901_a_());
keys.add(0, BLOCKS.func_240901_a_());
keys.add(1, ITEMS.func_240901_a_());
for (int i = 0, keysSize = keys.size(); i < keysSize; i++) {
final ResourceLocation rl = keys.get(i);
if (!filter.test(rl)) continue;
ForgeRegistry<?> reg = RegistryManager.ACTIVE.getRegistry(rl);
reg.unfreeze();
StartupMessageManager.modLoaderConsumer().ifPresent(s->s.accept("REGISTERING "+rl));
final RegistryEvent.Register<?> registerEvent = reg.getRegisterEvent(rl);
lifecycleEventProvider.setCustomEventSupplier(() -> registerEvent);
lifecycleEventProvider.changeProgression(LifecycleEventProvider.LifecycleEvent.Progression.STAY);
if (i==keysSize-1) lifecycleEventProvider.changeProgression(LifecycleEventProvider.LifecycleEvent.Progression.NEXT);
eventDispatcher.accept(lifecycleEventProvider);
reg.freeze();
LOGGER.debug(REGISTRIES,"Applying holder lookups: {}", rl.toString());
ObjectHolderRegistry.applyObjectHolders(rl::equals);
LOGGER.debug(REGISTRIES,"Holder lookups applied: {}", rl.toString());
}
}
/** /**
* Check a name for a domain prefix, and if not present infer it from the * Check a name for a domain prefix, and if not present infer it from the
* current active mod container. * current active mod container.

View file

@ -36,6 +36,6 @@ public class StencilEnableTest {
private void clientSetup(FMLClientSetupEvent event) { private void clientSetup(FMLClientSetupEvent event) {
if (ENABLED) if (ENABLED)
DeferredWorkQueue.runLater(() -> Minecraft.getInstance().getFramebuffer().enableStencil()); event.enqueueWork(() -> Minecraft.getInstance().getFramebuffer().enableStencil());
} }
} }