Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Bugfix/mod init #173

Merged
merged 7 commits into from
Aug 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion patchwork-dispatcher/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ dependencies {
implementation project(path: ':patchwork-fml', configuration: 'dev')
implementation project(path: ':patchwork-registries', configuration: 'dev')
implementation project(path: ':patchwork-events-lifecycle', configuration: 'dev')
implementation project(path: ':patchwork-events-rendering', configuration: 'dev')
implementation project(path: ':patchwork-model-loader', configuration: 'dev')
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@

package net.patchworkmc.impl;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
Expand All @@ -45,40 +45,37 @@
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
import net.minecraftforge.registries.ForgeRegistries;

import net.minecraft.client.MinecraftClient;
import net.minecraft.server.dedicated.DedicatedServer;

import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader;

import net.patchworkmc.api.ForgeInitializer;
import net.patchworkmc.impl.event.lifecycle.LifecycleEvents;
import net.patchworkmc.impl.event.render.RenderEvents;
import net.patchworkmc.impl.modelloader.ModelEventDispatcher;
import net.patchworkmc.impl.registries.RegistryEventDispatcher;

public class Patchwork implements ModInitializer {
public class Patchwork {
private static final Logger LOGGER = LogManager.getLogger(Patchwork.class);

private static void dispatch(Map<ForgeInitializer, FMLModContainer> mods, Event event) {
private static void dispatch(Collection<FMLModContainer> mods, Event event) {
dispatch(mods, container -> event);
}

private static void dispatch(Map<ForgeInitializer, FMLModContainer> mods, Function<ModContainer, Event> provider) {
for (FMLModContainer container : mods.values()) {
/**
* Fire the specific event for all ModContainers on the {@link Mod.EventBusSubscriber.Bus.MOD} Event bus.
*/
private static void dispatch(Collection<FMLModContainer> mods, Function<ModContainer, Event> provider) {
for (FMLModContainer container : mods) {
ModLoadingContext.get().setActiveContainer(container, new FMLJavaModLoadingContext(container));

Event event = provider.apply(container);
LOGGER.debug("Firing event for modid {} : {}", container.getModId(), event.toString());
container.getEventBus().post(event);
container.patchwork$acceptEvent(event);
LOGGER.debug("Fired event for modid {} : {}", container.getModId(), event.toString());

ModLoadingContext.get().setActiveContainer(null, "minecraft");
}
}

@Override
public void onInitialize() {
public static void gatherAndInitializeMods() {
ForgeRegistries.init();

Map<ForgeInitializer, FMLModContainer> mods = new HashMap<>();
Expand All @@ -102,6 +99,7 @@ public void onInitialize() {
ModLoadingContext.get().setActiveContainer(container, new FMLJavaModLoadingContext(container));

try {
// TODO: Supposed to call "container.setMod()" here, but this requires a WIP Patchwork-Patcher feature.
initializer.onForgeInitialize();
} catch (Throwable t) {
if (error == null) {
Expand Down Expand Up @@ -150,27 +148,65 @@ public void onInitialize() {
ModList.get().setLoadedMods(mods.values());
// Send initialization events

dispatch(mods, new RegistryEvent.NewRegistry());
RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods, event));
dispatch(mods, FMLCommonSetupEvent::new);

DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
ModelEventDispatcher.fireModelRegistryEvent();
dispatch(mods, container -> new FMLClientSetupEvent(MinecraftClient::getInstance, container));
RenderEvents.registerEventDispatcher(event -> dispatch(mods, event));
});
dispatch(mods.values(), new RegistryEvent.NewRegistry());
RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods.values(), event));
}

DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, () -> () -> {
Object gameInstance = FabricLoader.getInstance().getGameInstance();
Supplier<DedicatedServer> supplier = () -> (DedicatedServer) gameInstance;
/**
* This is called on the ResourceLoader's thread when a resource loading happens, i.e. during client start-up or F3+T is pressed.
* Forge fires the FMLCommonSetupEvent and LifeCycleEvents(FMLClientSetupEvent and FMLDedicatedServerSetupEvent) on its own thread in parallel. Sequence cannot be guaranteed.
* IMPORTANT: In Patchwork, we fire all events on the main thread (Client Thread or Server Thread).
* @param lifeCycleEvent
* @param preSidedRunnable Fired before the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed.
* @param postSidedRunnable Fired after the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed.
*/
public static void loadMods(Function<ModContainer, Event> lifeCycleEvent, Consumer<Consumer<Supplier<Event>>> preSidedRunnable, Consumer<Consumer<Supplier<Event>>> postSidedRunnable) {
List<FMLModContainer> mods = ModList.get().applyForEachModContainer(m -> (FMLModContainer) m).collect(Collectors.toList());

// Loading mod config
// TODO: Load client and common configs here

// Mod setup: SETUP
dispatch(mods, FMLCommonSetupEvent::new);
// Mod setup: SIDED SETUP
preSidedRunnable.accept(c -> dispatch(mods, c.get()));
dispatch(mods, lifeCycleEvent);
postSidedRunnable.accept(c -> dispatch(mods, c.get()));
// Mod setup complete
}

dispatch(mods, container -> new FMLDedicatedServerSetupEvent(supplier, container));
});
/**
* In Patchwork, we fire all of following events on the main thread (Client Thread or Server Thread).
*/
public static void finishMods() {
List<FMLModContainer> mods = ModList.get().applyForEachModContainer(m -> (FMLModContainer) m).collect(Collectors.toList());

// Mod setup: ENQUEUE IMC
dispatch(mods, InterModEnqueueEvent::new);
// Mod setup: PROCESS IMC
dispatch(mods, InterModProcessEvent::new);
LifecycleEvents.setLoadCompleteCallback(() -> dispatch(mods, FMLLoadCompleteEvent::new));
// Mod setup: Final completion
dispatch(mods, FMLLoadCompleteEvent::new);
// Freezing data, TODO: do we need freezing?
// GameData.freezeData();
// NetworkRegistry.lock();
}

public static void beginServerModLoading() {
Object gameInstance = FabricLoader.getInstance().getGameInstance();
Supplier<DedicatedServer> supplier = () -> (DedicatedServer) gameInstance;

LOGGER.debug("Patchwork Dedicated Server Mod Loader: Start mod loading.");
Patchwork.gatherAndInitializeMods();
Patchwork.loadMods(container -> new FMLDedicatedServerSetupEvent(supplier, container), dummy -> { }, dummy -> { });
}

public static void endOfServerModLoading() {
LOGGER.debug("Patchwork Dedicated Server Mod Loader: Finish mod loading.");
Patchwork.finishMods();

LOGGER.debug("Patchwork Dedicated Server Mod Loader: Complete mod loading");
// Assume there's no error.
MinecraftForge.EVENT_BUS.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Minecraft Forge, Patchwork Project
* Copyright (c) 2016-2020, 2019-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.patchworkmc.impl;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;

import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceReloadListener;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.ClientBuiltinResourcePackProvider;
import net.minecraft.client.resource.ClientResourcePackProfile;
import net.minecraft.resource.ReloadableResourceManager;
import net.minecraft.resource.ResourcePackManager;

public class PatchworkClientModLoader {
private static final Logger LOGGER = LogManager.getLogger(PatchworkClientModLoader.class);
private static boolean loading;
private static MinecraftClient mc;

public static void begin(final MinecraftClient minecraft, final ResourcePackManager<ClientResourcePackProfile> defaultResourcePacks,
final ReloadableResourceManager mcResourceManager, ClientBuiltinResourcePackProvider metadataSerializer) {
loading = true;
PatchworkClientModLoader.mc = minecraft;
Patchwork.gatherAndInitializeMods();
mcResourceManager.registerListener(PatchworkClientModLoader::onReload);
}

/**
* @param syncExecutor The main thread executor
*/
private static CompletableFuture<Void> onReload(final ResourceReloadListener.Synchronizer stage, final ResourceManager resourceManager,
final Profiler prepareProfiler, final Profiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
return CompletableFuture.runAsync(() -> startModLoading(syncExecutor), asyncExecutor)
.thenCompose(stage::whenPrepared)
.thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor);
}

private static void startModLoading(Executor mainThreadExecutor) {
LOGGER.debug("Patchwork Client Mod Loader: Start mod loading.");
mainThreadExecutor.execute(() -> Patchwork.loadMods(container -> new FMLClientSetupEvent(() -> PatchworkClientModLoader.mc, container),
PatchworkClientModLoader::preSidedRunnable, PatchworkClientModLoader::postSidedRunnable));
}

private static void preSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
perModContainerEventProcessor.accept(ModelRegistryEvent::new);
}

private static void postSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
}

private static void finishModLoading(Executor executor) {
LOGGER.debug("Patchwork Client Mod Loader: Finish mod loading.");
Patchwork.finishMods();
loading = false;
// reload game settings on main thread
executor.execute(() -> mc.options.load());
}

/**
* @return true if an error occurred so that we can cancel the normal title screen.
*/
public static boolean completeModLoading() {
LOGGER.debug("Patchwork Client Mod Loader: Complete mod loading");
// Assume there's no error.
MinecraftForge.EVENT_BUS.start();
return false;
}

// TODO: Reserved for future use
public static void onResourceReloadComplete(boolean errorFree) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Minecraft Forge, Patchwork Project
* Copyright (c) 2016-2020, 2019-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.patchworkmc.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import net.minecraft.client.MinecraftClient;
import net.minecraft.resource.ReloadableResourceManager;

import net.patchworkmc.impl.PatchworkClientModLoader;

@Mixin(MinecraftClient.class)
public abstract class MixinMinecraftClient {
@Shadow
private ReloadableResourceManager resourceManager;

@Inject(method = "init", at = @At(value = "INVOKE", shift = Shift.BEFORE, ordinal = 0, target = "net/minecraft/resource/ResourcePackManager.scanPacks()V"))
private void initForgeModsOnClient(CallbackInfo ci) {
MinecraftClient me = (MinecraftClient) (Object) this;
PatchworkClientModLoader.begin(me, me.getResourcePackManager(), resourceManager, me.getResourcePackDownloader());
}

// this.setOverlay(new SplashScreen(this, this.resourceManager.beginInitialMonitoredReload(SystemUtil.getServerWorkerExecutor(), this, voidFuture), () -> {
// if (SharedConstants.isDevelopment) this.checkGameData();
// + if (net.minecraftforge.fml.client.ClientModLoader.completeModLoading()) return; // Do not overwrite the error sceen
// + // Show either ConnectScreen or TitleScreen
// }
@Inject(method = "method_18504", at = @At("RETURN"))
private void onResourceReloadComplete(CallbackInfo ci) {
PatchworkClientModLoader.onResourceReloadComplete(!PatchworkClientModLoader.completeModLoading());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

package net.patchworkmc.mixin.event.lifecycle;
package net.patchworkmc.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
import net.minecraft.server.integrated.IntegratedServer;

import net.patchworkmc.impl.event.lifecycle.LifecycleEvents;
import net.patchworkmc.impl.Patchwork;

/**
* Mixes into {@link IntegratedServer} and {@link MinecraftDedicatedServer} in order to implement
* {@link net.minecraftforge.fml.event.server.FMLServerStartingEvent}. This event fires right before the implementations
* return <code>true</code> from <code>setupServer</code>. Returning <code>false</code> from the callback cancels the
* server's startup, however, it's important to note that this event isn't actually cancellable in Forge!
*/
@Mixin({IntegratedServer.class, MinecraftDedicatedServer.class})
public class MixinMinecraftServerSubclass {
@Inject(method = "setupServer", at = @At("RETURN"))
private void hookSetupEnd(CallbackInfoReturnable<Boolean> callback) {
LifecycleEvents.handleServerStarting((MinecraftServer) (Object) this);
@Mixin(MinecraftDedicatedServer.class)
public abstract class MixinMinecraftDedicatedServer {
@Inject(method = "setupServer", at = @At(value = "INVOKE", shift = Shift.AFTER, ordinal = 0, target = "org/apache/logging/log4j/Logger.info(Ljava/lang/String;)V"))
private void initForgeModsOnServer(CallbackInfoReturnable<Boolean> ci) {
Patchwork.beginServerModLoading();
}

@Inject(method = "setupServer", at = @At(value = "NEW", shift = Shift.BEFORE, ordinal = 0, target = "net/minecraft/server/dedicated/DedicatedPlayerManager"))
private void endOfModLoading(CallbackInfoReturnable<Boolean> ci) {
Patchwork.endOfServerModLoading();
}
}
11 changes: 5 additions & 6 deletions patchwork-dispatcher/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@
"license": "LGPL-2.1-only",
"icon": "assets/patchwork-dispatcher/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"net.patchworkmc.impl.Patchwork"
]
},
"depends": {
"patchwork-api-base": "*",
"patchwork-fml": "*",
"patchwork-registries": "*",
"patchwork-events-lifecycle": "*"
"patchwork-events-lifecycle": "*",
"patchwork-model-loader": "*"
},
"mixins": [
"patchwork-dispatcher.mixins.json"
],
"custom": {
"modmenu:api": true,
"modmenu:parent": "patchwork"
Expand Down
Loading