Redo event dispatch, removes a bunch of nonsense, and tries to integrate with the vanilla CF system where possible
Signed-off-by: cpw <cpw+github@weeksfamily.ca>
This commit is contained in:
parent
ff752ab14b
commit
d76ee4edd7
24 changed files with 570 additions and 617 deletions
|
@ -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));
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -22,22 +22,21 @@ 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.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
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 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 +51,60 @@ 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TaskInfo
|
||||||
|
{
|
||||||
|
public final IModInfo owner;
|
||||||
|
public final Runnable task;
|
||||||
|
|
||||||
|
private TaskInfo(IModInfo owner, Runnable task) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,27 +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 (modLoadingStage == event.fromStage())
|
return CompletableFuture
|
||||||
{
|
.runAsync(() -> {
|
||||||
try
|
ModLoadingContext.get().setActiveContainer(target, target.contextExtension.get());
|
||||||
{
|
target.activityMap.getOrDefault(target.modLoadingStage, ()->{}).run();
|
||||||
ModLoadingContext.get().setActiveContainer(this, contextExtension.get());
|
eventDispatcher.apply(target::acceptEvent).accept(eventGenerator.apply(target));
|
||||||
triggerMap.getOrDefault(modLoadingStage, e->{}).accept(event);
|
}, executor)
|
||||||
modLoadingStage = event.toStage();
|
.whenComplete((mc, exception) -> {
|
||||||
|
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,5 +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) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,38 +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)));
|
final RuntimeException accumulator = new RuntimeException();
|
||||||
while (ticker != null && !parallelTask.isDone()) {
|
cf.completeExceptionally(accumulator);
|
||||||
executor.execute(ticker);
|
throwables.forEach(exception -> {
|
||||||
|
if (exception instanceof CompletionException) {
|
||||||
|
exception = exception.getCause();
|
||||||
}
|
}
|
||||||
parallelTask.get();
|
if (exception.getSuppressed().length!=0) {
|
||||||
|
Arrays.stream(exception.getSuppressed()).forEach(accumulator::addSuppressed);
|
||||||
|
} else {
|
||||||
|
accumulator.addSuppressed(exception);
|
||||||
}
|
}
|
||||||
catch (InterruptedException | ExecutionException e)
|
});
|
||||||
{
|
return cf;
|
||||||
LOGGER.error(LOADING, "Encountered an exception during parallel processing - sleeping 10 seconds to wait for jobs to finish", e);
|
|
||||||
errorHandler.accept(Collections.singletonList(new UncaughtModLoadingException(lifecycleEvent.fromStage(), e)));
|
|
||||||
modLoadingThreadPool.awaitQuiescence(10, TimeUnit.SECONDS);
|
|
||||||
if (!modLoadingThreadPool.isQuiescent()) {
|
|
||||||
LOGGER.fatal(LOADING, "The parallel pool has failed to quiesce correctly, forcing a shutdown. There is something really wrong here");
|
|
||||||
modLoadingThreadPool.shutdownNow();
|
|
||||||
throw new RuntimeException("Forge played \"STOP IT NOW MODS!\" - it was \"NOT VERY EFFECTIVE\"");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeferredWorkQueue.runTasks(lifecycleEvent.fromStage(), errorHandler);
|
|
||||||
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
|
static <V> CompletableFuture<List<Map.Entry<V, Throwable>>> gather(List<? extends CompletableFuture<? extends V>> futures) {
|
||||||
|
List<Map.Entry<V, Throwable>> list = new ArrayList<>(futures.size());
|
||||||
|
CompletableFuture<?>[] results = new CompletableFuture[futures.size()];
|
||||||
|
futures.forEach(future -> {
|
||||||
|
int i = list.size();
|
||||||
|
list.add(null);
|
||||||
|
results[i] = future.whenComplete((result, exception) -> list.set(i, new AbstractMap.SimpleImmutableEntry<>(result, exception)));
|
||||||
|
});
|
||||||
|
return CompletableFuture.allOf(results).handle((r, th)->null).thenApply(res -> list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLoadedMods(final List<ModContainer> modContainers)
|
void setLoadedMods(final List<ModContainer> modContainers)
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -147,37 +155,22 @@ 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"));
|
|
||||||
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());
|
||||||
|
@ -195,31 +188,74 @@ public class ModLoader
|
||||||
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);
|
||||||
|
statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP"));
|
||||||
|
dispatchAndHandleError(ModLoadingStage.SIDED_SETUP, syncExecutor, parallelExecutor, periodicTask, beforeSidedEvent, afterSidedEvent);
|
||||||
|
statusConsumer.ifPresent(c->c.accept("Mod setup complete"));
|
||||||
}
|
}
|
||||||
if (!loadingExceptions.isEmpty()) {
|
|
||||||
LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", event, loadingExceptions.size());
|
public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask)
|
||||||
throw new LoadingFailedException(loadingExceptions);
|
{
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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()) {
|
||||||
|
ticker.run();
|
||||||
|
syncExecutor.drive();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
transition.join();
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
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 void accumulateErrors(List<ModLoadingException> errors) {
|
|
||||||
loadingExceptions.addAll(errors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ModContainer> buildMods(final ModFile modFile, final TransformingClassLoader modClassLoader)
|
private List<ModContainer> buildMods(final ModFile modFile, final TransformingClassLoader modClassLoader)
|
||||||
|
@ -229,7 +265,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,25 +292,10 @@ public class ModLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postEvent(Event e) {
|
public <T extends Event & IModBusEvent> void postEvent(T e) {
|
||||||
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e));
|
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finishMods(Executor mainThreadExecutor)
|
|
||||||
{
|
|
||||||
DeferredWorkQueue.workExecutor = mainThreadExecutor;
|
|
||||||
statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC"));
|
|
||||||
dispatchAndHandleError(LifecycleEventProvider.ENQUEUE_IMC, mainThreadExecutor, null);
|
|
||||||
statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC"));
|
|
||||||
dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC, mainThreadExecutor, null);
|
|
||||||
statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion"));
|
|
||||||
dispatchAndHandleError(LifecycleEventProvider.COMPLETE, mainThreadExecutor, null);
|
|
||||||
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())));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ModLoadingWarning> getWarnings()
|
public List<ModLoadingWarning> getWarnings()
|
||||||
{
|
{
|
||||||
return ImmutableList.copyOf(this.loadingWarnings);
|
return ImmutableList.copyOf(this.loadingWarnings);
|
||||||
|
@ -292,15 +313,15 @@ public class ModLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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;
|
// if (mods.contains("minecraft") && mods.size() == 1) return;
|
||||||
LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
// LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
||||||
runningDataGen = true;
|
// runningDataGen = true;
|
||||||
Bootstrap.register();
|
// Bootstrap.register();
|
||||||
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat);
|
// dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat);
|
||||||
existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
// existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
||||||
gatherAndInitializeMods(() -> {});
|
// gatherAndInitializeMods(() -> {});
|
||||||
dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {});
|
// dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {});
|
||||||
dataGeneratorConfig.runAll();
|
// dataGeneratorConfig.runAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Function<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
|
public Function<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
|
||||||
|
|
|
@ -19,34 +19,140 @@
|
||||||
|
|
||||||
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);
|
GATHERDATA();
|
||||||
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
final DeferredWorkQueue 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 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
103
src/main/java/net/minecraftforge/fml/ModWorkManager.java
Normal file
103
src/main/java/net/minecraftforge/fml/ModWorkManager.java
Normal 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() {
|
||||||
|
if (!selfDriven()) {
|
||||||
|
while (driveOne())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,10 +22,7 @@ package net.minecraftforge.fml;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
import net.minecraft.server.dedicated.DedicatedServer;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
|
||||||
import net.minecraftforge.fml.client.ClientHooks;
|
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 net.minecraftforge.fml.loading.FMLEnvironment;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -38,10 +35,6 @@ public enum SidedProvider
|
||||||
c->c.get().getDataFixer(),
|
c->c.get().getDataFixer(),
|
||||||
s->s.get().getDataFixer(),
|
s->s.get().getDataFixer(),
|
||||||
()-> { throw new UnsupportedOperationException(); }),
|
()-> { 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(
|
STRIPCHARS(
|
||||||
(Function<Supplier<Minecraft>, Function<String, String>>)c-> ClientHooks::stripSpecialChars,
|
(Function<Supplier<Minecraft>, Function<String, String>>)c-> ClientHooks::stripSpecialChars,
|
||||||
s-> str->str,
|
s-> str->str,
|
||||||
|
|
|
@ -32,9 +32,22 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
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.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.minecraftforge.fml.BrandingControl;
|
||||||
|
import net.minecraftforge.fml.LoadingFailedException;
|
||||||
|
import net.minecraftforge.fml.LogicalSidedProvider;
|
||||||
|
import net.minecraftforge.fml.ModLoader;
|
||||||
|
import net.minecraftforge.fml.ModLoadingStage;
|
||||||
|
import net.minecraftforge.fml.ModLoadingWarning;
|
||||||
|
import net.minecraftforge.fml.ModWorkManager;
|
||||||
|
import net.minecraftforge.fml.SidedProvider;
|
||||||
|
import net.minecraftforge.fml.VersionChecker;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@ -48,19 +61,9 @@ import net.minecraft.resources.data.PackMetadataSection;
|
||||||
import net.minecraft.util.text.TranslationTextComponent;
|
import net.minecraft.util.text.TranslationTextComponent;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
|
||||||
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.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;
|
||||||
|
@ -90,17 +93,19 @@ public class ClientModLoader
|
||||||
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(), earlyLoaderGUI::renderTick)).run();
|
||||||
|
if (error == null) {
|
||||||
ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder);
|
ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder);
|
||||||
mcResourceManager.addReloadListener(ClientModLoader::onreload);
|
mcResourceManager.addReloadListener(ClientModLoader::onResourceReload);
|
||||||
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener());
|
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener());
|
||||||
ModelLoaderRegistry.init();
|
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) {
|
||||||
|
@ -114,26 +119,27 @@ public class ClientModLoader
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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), ()->{})).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());
|
||||||
ModelLoaderRegistry.initComplete();
|
ModelLoaderRegistry.initComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void preSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
|
private static void preSidedRunnable() {
|
||||||
perModContainerEventProcessor.accept(ModelRegistryEvent::new);
|
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, ()->{})).run();
|
||||||
loading = false;
|
loading = false;
|
||||||
// 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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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!"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,20 +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.fml.event.lifecycle.ModLifecycleEvent;
|
|
||||||
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.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static net.minecraftforge.fml.Logging.LOADING;
|
import static net.minecraftforge.fml.Logging.LOADING;
|
||||||
|
|
||||||
|
@ -55,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
|
||||||
|
@ -80,52 +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 (s) -> {}; }
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preinitMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void constructMod(LifecycleEventProvider.LifecycleEvent event)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -136,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());
|
||||||
|
@ -144,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +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 {
|
||||||
|
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) {
|
||||||
|
LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", e, this.getModId(), t);
|
||||||
|
throw new ModLoadingException(modInfo, modLoadingStage, "fml.modloading.errorduringevent", t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ 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.ModWorkManager;
|
||||||
import net.minecraftforge.fml.SidedProvider;
|
import net.minecraftforge.fml.SidedProvider;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
@ -46,9 +47,9 @@ public class ServerModLoader
|
||||||
});
|
});
|
||||||
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(), null, null, ()->{});
|
||||||
ModLoader.get().finishMods(Runnable::run);
|
ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
|
||||||
} catch (LoadingFailedException e) {
|
} catch (LoadingFailedException e) {
|
||||||
ServerModLoader.hasErrors = true;
|
ServerModLoader.hasErrors = true;
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -51,7 +51,6 @@ import net.minecraft.util.ObjectIntIdentityMap;
|
||||||
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;
|
||||||
|
@ -69,8 +68,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;
|
||||||
|
@ -91,9 +90,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;
|
||||||
|
|
||||||
|
@ -367,6 +365,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>
|
||||||
{
|
{
|
||||||
|
@ -832,47 +858,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);
|
|
||||||
keys.remove(ITEMS);
|
|
||||||
|
|
||||||
keys.add(0, BLOCKS);
|
|
||||||
keys.add(1, ITEMS);
|
|
||||||
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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue