166 lines
8.9 KiB
Java
166 lines
8.9 KiB
Java
/*
|
|
* 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 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.Collections;
|
|
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.Supplier;
|
|
import java.util.stream.Stream;
|
|
|
|
public enum ModLoadingStage
|
|
{
|
|
ERROR(),
|
|
VALIDATE(),
|
|
CONSTRUCT(FMLConstructModEvent.class),
|
|
CREATE_REGISTRIES(()->Stream.of(EventGenerator.fromFunction(RegistryEvent.NewRegistry::new))),
|
|
LOAD_REGISTRIES(GameData::generateRegistryEvents, GameData::preRegistryEventDispatch, GameData::postRegistryEventDispatch),
|
|
COMMON_SETUP(FMLCommonSetupEvent.class),
|
|
SIDED_SETUP(DistExecutor.unsafeRunForDist(()->()->FMLClientSetupEvent.class, ()->()->FMLDedicatedServerSetupEvent.class)),
|
|
ENQUEUE_IMC(InterModEnqueueEvent.class),
|
|
PROCESS_IMC(InterModProcessEvent.class),
|
|
COMPLETE(FMLLoadCompleteEvent.class),
|
|
DONE();
|
|
|
|
private final Supplier<Stream<EventGenerator<?>>> eventFunctionStream;
|
|
private final Optional<Class<? extends ParallelDispatchEvent>> parallelEventClass;
|
|
private final ThreadSelector threadSelector;
|
|
private final BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator;
|
|
private final BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> preDispatchHook;
|
|
private final BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> postDispatchHook;
|
|
private DeferredWorkQueue deferredWorkQueue;
|
|
|
|
ModLoadingStage(Class<? extends ParallelDispatchEvent> parallelClass) {
|
|
final EventGenerator<?> event = EventGenerator.fromFunction(LamdbaExceptionUtils.rethrowFunction((ModContainer mc) -> parallelClass.getConstructor(ModContainer.class).newInstance(mc)));
|
|
this.eventFunctionStream = ()->Stream.of(event);
|
|
this.threadSelector = ThreadSelector.PARALLEL;
|
|
this.parallelEventClass = Optional.of(parallelClass);
|
|
deferredWorkQueue = new DeferredWorkQueue(this, parallelClass);
|
|
this.finalActivityGenerator = (e, prev) -> prev.thenApplyAsync((List<Throwable> t) -> {
|
|
deferredWorkQueue.runTasks();
|
|
return t;
|
|
}, e);
|
|
this.preDispatchHook = (t,f)->CompletableFuture.completedFuture(Collections.emptyList());
|
|
this.postDispatchHook = (t,f)->CompletableFuture.completedFuture(Collections.emptyList());
|
|
}
|
|
|
|
ModLoadingStage(Supplier<Stream<EventGenerator<?>>> eventStream) {
|
|
this(eventStream, (t,f)->CompletableFuture.completedFuture(Collections.emptyList()), (t,f)->CompletableFuture.completedFuture(Collections.emptyList()));
|
|
}
|
|
@SuppressWarnings("unchecked")
|
|
<T extends Event & IModBusEvent> ModLoadingStage(Supplier<Stream<EventGenerator<?>>> eventStream, final BiFunction<Executor, ? extends EventGenerator<T>, CompletableFuture<List<Throwable>>> preDispatchHook, final BiFunction<Executor, ? extends EventGenerator<T>, CompletableFuture<List<Throwable>>> postDispatchHook) {
|
|
this.eventFunctionStream = eventStream;
|
|
this.parallelEventClass = Optional.empty();
|
|
this.threadSelector = ThreadSelector.SYNC;
|
|
this.preDispatchHook = preDispatchHook;
|
|
this.postDispatchHook = postDispatchHook;
|
|
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<>();
|
|
this.eventFunctionStream.get()
|
|
.map(f->(EventGenerator<T>)f)
|
|
.reduce((head, tail)-> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, cfs, head, ModLoadingStage::currentState, tail))
|
|
.ifPresent(last-> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, cfs, last, ModLoadingStage::nextState, null));
|
|
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);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private <T extends Event & IModBusEvent> EventGenerator<T> addCompletableFutureTaskForModDispatch(final Executor syncExecutor, final Executor parallelExecutor, final List<CompletableFuture<List<Throwable>>> completeableFutures, final EventGenerator<T> eventGenerator, BiFunction<ModLoadingStage, Throwable, ModLoadingStage> nextState, final EventGenerator<T> nextGenerator) {
|
|
completeableFutures.add(((BiFunction<Executor, EventGenerator<T>, CompletableFuture<List<Throwable>>>)preDispatchHook).apply(threadSelector.apply(syncExecutor, parallelExecutor), eventGenerator));
|
|
completeableFutures.add(ModList.get().futureVisitor(eventGenerator, nextState).apply(threadSelector.apply(syncExecutor, parallelExecutor)));
|
|
completeableFutures.add(((BiFunction<Executor, EventGenerator<T>, CompletableFuture<List<Throwable>>>)postDispatchHook).apply(threadSelector.apply(syncExecutor, parallelExecutor), eventGenerator));
|
|
return nextGenerator;
|
|
}
|
|
|
|
ModLoadingStage nextState(Throwable exception) {
|
|
return exception != null ? ERROR : values()[this.ordinal()+1];
|
|
}
|
|
|
|
ModLoadingStage currentState(Throwable exception) {
|
|
return exception != null ? ERROR : this;
|
|
}
|
|
|
|
public Optional<Class<? extends ParallelDispatchEvent>> getParallelEventClass() {
|
|
return parallelEventClass;
|
|
}
|
|
enum ThreadSelector implements BinaryOperator<Executor> {
|
|
SYNC((sync, parallel)->sync),
|
|
PARALLEL((sync, parallel)->parallel);
|
|
|
|
private final BinaryOperator<Executor> selector;
|
|
|
|
ThreadSelector(final BinaryOperator<Executor> selector) {
|
|
this.selector = selector;
|
|
}
|
|
|
|
@Override
|
|
public Executor apply(final Executor sync, final Executor parallel) {
|
|
return this.selector.apply(sync, parallel);
|
|
}
|
|
}
|
|
|
|
public DeferredWorkQueue getDeferredWorkQueue() {
|
|
return deferredWorkQueue;
|
|
}
|
|
|
|
public interface EventGenerator<T extends Event & IModBusEvent> extends Function<ModContainer, T> {
|
|
static <FN extends Event & IModBusEvent> EventGenerator<FN> fromFunction(Function<ModContainer, FN> fn) {
|
|
return fn::apply;
|
|
}
|
|
}
|
|
}
|