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

Commit a86a132

Browse files
authored
Bugfix/mod init (#173)
* Construct Forge mods at the correct place * Remove the unused Fabric mod entry * Remove debug outputs & add comments * Fix the order of events on client * Fix crashing on dedicated server * Fix FMLServerStartingEvent to accommodate the new mod loading procedure * Fix a mixin applied on the wrong side
1 parent 146f43b commit a86a132

17 files changed

Lines changed: 290 additions & 93 deletions

File tree

patchwork-dispatcher/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ dependencies {
66
implementation project(path: ':patchwork-fml', configuration: 'dev')
77
implementation project(path: ':patchwork-registries', configuration: 'dev')
88
implementation project(path: ':patchwork-events-lifecycle', configuration: 'dev')
9-
implementation project(path: ':patchwork-events-rendering', configuration: 'dev')
109
implementation project(path: ':patchwork-model-loader', configuration: 'dev')
1110
}

patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,23 @@
1919

2020
package net.patchworkmc.impl;
2121

22+
import java.util.Collection;
2223
import java.util.HashMap;
2324
import java.util.List;
2425
import java.util.Map;
26+
import java.util.function.Consumer;
2527
import java.util.function.Function;
2628
import java.util.function.Supplier;
29+
import java.util.stream.Collectors;
2730

2831
import org.apache.logging.log4j.LogManager;
2932
import org.apache.logging.log4j.Logger;
30-
import net.minecraftforge.api.distmarker.Dist;
3133
import net.minecraftforge.common.MinecraftForge;
3234
import net.minecraftforge.event.RegistryEvent;
3335
import net.minecraftforge.eventbus.api.Event;
34-
import net.minecraftforge.fml.DistExecutor;
3536
import net.minecraftforge.fml.ModContainer;
3637
import net.minecraftforge.fml.ModList;
3738
import net.minecraftforge.fml.ModLoadingContext;
38-
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
3939
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
4040
import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
4141
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
@@ -45,40 +45,37 @@
4545
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
4646
import net.minecraftforge.registries.ForgeRegistries;
4747

48-
import net.minecraft.client.MinecraftClient;
4948
import net.minecraft.server.dedicated.DedicatedServer;
5049

51-
import net.fabricmc.api.ModInitializer;
5250
import net.fabricmc.loader.api.FabricLoader;
5351

5452
import net.patchworkmc.api.ForgeInitializer;
55-
import net.patchworkmc.impl.event.lifecycle.LifecycleEvents;
56-
import net.patchworkmc.impl.event.render.RenderEvents;
57-
import net.patchworkmc.impl.modelloader.ModelEventDispatcher;
5853
import net.patchworkmc.impl.registries.RegistryEventDispatcher;
5954

60-
public class Patchwork implements ModInitializer {
55+
public class Patchwork {
6156
private static final Logger LOGGER = LogManager.getLogger(Patchwork.class);
6257

63-
private static void dispatch(Map<ForgeInitializer, FMLModContainer> mods, Event event) {
58+
private static void dispatch(Collection<FMLModContainer> mods, Event event) {
6459
dispatch(mods, container -> event);
6560
}
6661

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

7169
Event event = provider.apply(container);
7270
LOGGER.debug("Firing event for modid {} : {}", container.getModId(), event.toString());
73-
container.getEventBus().post(event);
71+
container.patchwork$acceptEvent(event);
7472
LOGGER.debug("Fired event for modid {} : {}", container.getModId(), event.toString());
7573

7674
ModLoadingContext.get().setActiveContainer(null, "minecraft");
7775
}
7876
}
7977

80-
@Override
81-
public void onInitialize() {
78+
public static void gatherAndInitializeMods() {
8279
ForgeRegistries.init();
8380

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

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

153-
dispatch(mods, new RegistryEvent.NewRegistry());
154-
RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods, event));
155-
dispatch(mods, FMLCommonSetupEvent::new);
156-
157-
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
158-
ModelEventDispatcher.fireModelRegistryEvent();
159-
dispatch(mods, container -> new FMLClientSetupEvent(MinecraftClient::getInstance, container));
160-
RenderEvents.registerEventDispatcher(event -> dispatch(mods, event));
161-
});
151+
dispatch(mods.values(), new RegistryEvent.NewRegistry());
152+
RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods.values(), event));
153+
}
162154

163-
DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, () -> () -> {
164-
Object gameInstance = FabricLoader.getInstance().getGameInstance();
165-
Supplier<DedicatedServer> supplier = () -> (DedicatedServer) gameInstance;
155+
/**
156+
* This is called on the ResourceLoader's thread when a resource loading happens, i.e. during client start-up or F3+T is pressed.
157+
* Forge fires the FMLCommonSetupEvent and LifeCycleEvents(FMLClientSetupEvent and FMLDedicatedServerSetupEvent) on its own thread in parallel. Sequence cannot be guaranteed.
158+
* IMPORTANT: In Patchwork, we fire all events on the main thread (Client Thread or Server Thread).
159+
* @param lifeCycleEvent
160+
* @param preSidedRunnable Fired before the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed.
161+
* @param postSidedRunnable Fired after the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed.
162+
*/
163+
public static void loadMods(Function<ModContainer, Event> lifeCycleEvent, Consumer<Consumer<Supplier<Event>>> preSidedRunnable, Consumer<Consumer<Supplier<Event>>> postSidedRunnable) {
164+
List<FMLModContainer> mods = ModList.get().applyForEachModContainer(m -> (FMLModContainer) m).collect(Collectors.toList());
165+
166+
// Loading mod config
167+
// TODO: Load client and common configs here
168+
169+
// Mod setup: SETUP
170+
dispatch(mods, FMLCommonSetupEvent::new);
171+
// Mod setup: SIDED SETUP
172+
preSidedRunnable.accept(c -> dispatch(mods, c.get()));
173+
dispatch(mods, lifeCycleEvent);
174+
postSidedRunnable.accept(c -> dispatch(mods, c.get()));
175+
// Mod setup complete
176+
}
166177

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

184+
// Mod setup: ENQUEUE IMC
170185
dispatch(mods, InterModEnqueueEvent::new);
186+
// Mod setup: PROCESS IMC
171187
dispatch(mods, InterModProcessEvent::new);
172-
LifecycleEvents.setLoadCompleteCallback(() -> dispatch(mods, FMLLoadCompleteEvent::new));
188+
// Mod setup: Final completion
189+
dispatch(mods, FMLLoadCompleteEvent::new);
190+
// Freezing data, TODO: do we need freezing?
191+
// GameData.freezeData();
192+
// NetworkRegistry.lock();
193+
}
194+
195+
public static void beginServerModLoading() {
196+
Object gameInstance = FabricLoader.getInstance().getGameInstance();
197+
Supplier<DedicatedServer> supplier = () -> (DedicatedServer) gameInstance;
198+
199+
LOGGER.debug("Patchwork Dedicated Server Mod Loader: Start mod loading.");
200+
Patchwork.gatherAndInitializeMods();
201+
Patchwork.loadMods(container -> new FMLDedicatedServerSetupEvent(supplier, container), dummy -> { }, dummy -> { });
202+
}
203+
204+
public static void endOfServerModLoading() {
205+
LOGGER.debug("Patchwork Dedicated Server Mod Loader: Finish mod loading.");
206+
Patchwork.finishMods();
173207

208+
LOGGER.debug("Patchwork Dedicated Server Mod Loader: Complete mod loading");
209+
// Assume there's no error.
174210
MinecraftForge.EVENT_BUS.start();
175211
}
176212
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.impl;
21+
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.Executor;
24+
import java.util.function.Consumer;
25+
import java.util.function.Supplier;
26+
27+
import org.apache.logging.log4j.LogManager;
28+
import org.apache.logging.log4j.Logger;
29+
import net.minecraftforge.client.event.ModelRegistryEvent;
30+
import net.minecraftforge.common.MinecraftForge;
31+
import net.minecraftforge.eventbus.api.Event;
32+
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
33+
34+
import net.minecraft.resource.ResourceManager;
35+
import net.minecraft.resource.ResourceReloadListener;
36+
import net.minecraft.util.profiler.Profiler;
37+
import net.minecraft.client.MinecraftClient;
38+
import net.minecraft.client.resource.ClientBuiltinResourcePackProvider;
39+
import net.minecraft.client.resource.ClientResourcePackProfile;
40+
import net.minecraft.resource.ReloadableResourceManager;
41+
import net.minecraft.resource.ResourcePackManager;
42+
43+
public class PatchworkClientModLoader {
44+
private static final Logger LOGGER = LogManager.getLogger(PatchworkClientModLoader.class);
45+
private static boolean loading;
46+
private static MinecraftClient mc;
47+
48+
public static void begin(final MinecraftClient minecraft, final ResourcePackManager<ClientResourcePackProfile> defaultResourcePacks,
49+
final ReloadableResourceManager mcResourceManager, ClientBuiltinResourcePackProvider metadataSerializer) {
50+
loading = true;
51+
PatchworkClientModLoader.mc = minecraft;
52+
Patchwork.gatherAndInitializeMods();
53+
mcResourceManager.registerListener(PatchworkClientModLoader::onReload);
54+
}
55+
56+
/**
57+
* @param syncExecutor The main thread executor
58+
*/
59+
private static CompletableFuture<Void> onReload(final ResourceReloadListener.Synchronizer stage, final ResourceManager resourceManager,
60+
final Profiler prepareProfiler, final Profiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
61+
return CompletableFuture.runAsync(() -> startModLoading(syncExecutor), asyncExecutor)
62+
.thenCompose(stage::whenPrepared)
63+
.thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor);
64+
}
65+
66+
private static void startModLoading(Executor mainThreadExecutor) {
67+
LOGGER.debug("Patchwork Client Mod Loader: Start mod loading.");
68+
mainThreadExecutor.execute(() -> Patchwork.loadMods(container -> new FMLClientSetupEvent(() -> PatchworkClientModLoader.mc, container),
69+
PatchworkClientModLoader::preSidedRunnable, PatchworkClientModLoader::postSidedRunnable));
70+
}
71+
72+
private static void preSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
73+
perModContainerEventProcessor.accept(ModelRegistryEvent::new);
74+
}
75+
76+
private static void postSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
77+
}
78+
79+
private static void finishModLoading(Executor executor) {
80+
LOGGER.debug("Patchwork Client Mod Loader: Finish mod loading.");
81+
Patchwork.finishMods();
82+
loading = false;
83+
// reload game settings on main thread
84+
executor.execute(() -> mc.options.load());
85+
}
86+
87+
/**
88+
* @return true if an error occurred so that we can cancel the normal title screen.
89+
*/
90+
public static boolean completeModLoading() {
91+
LOGGER.debug("Patchwork Client Mod Loader: Complete mod loading");
92+
// Assume there's no error.
93+
MinecraftForge.EVENT_BUS.start();
94+
return false;
95+
}
96+
97+
// TODO: Reserved for future use
98+
public static void onResourceReloadComplete(boolean errorFree) {
99+
}
100+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin;
21+
22+
import org.spongepowered.asm.mixin.Mixin;
23+
import org.spongepowered.asm.mixin.Shadow;
24+
import org.spongepowered.asm.mixin.injection.At;
25+
import org.spongepowered.asm.mixin.injection.At.Shift;
26+
import org.spongepowered.asm.mixin.injection.Inject;
27+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
28+
29+
import net.minecraft.client.MinecraftClient;
30+
import net.minecraft.resource.ReloadableResourceManager;
31+
32+
import net.patchworkmc.impl.PatchworkClientModLoader;
33+
34+
@Mixin(MinecraftClient.class)
35+
public abstract class MixinMinecraftClient {
36+
@Shadow
37+
private ReloadableResourceManager resourceManager;
38+
39+
@Inject(method = "init", at = @At(value = "INVOKE", shift = Shift.BEFORE, ordinal = 0, target = "net/minecraft/resource/ResourcePackManager.scanPacks()V"))
40+
private void initForgeModsOnClient(CallbackInfo ci) {
41+
MinecraftClient me = (MinecraftClient) (Object) this;
42+
PatchworkClientModLoader.begin(me, me.getResourcePackManager(), resourceManager, me.getResourcePackDownloader());
43+
}
44+
45+
// this.setOverlay(new SplashScreen(this, this.resourceManager.beginInitialMonitoredReload(SystemUtil.getServerWorkerExecutor(), this, voidFuture), () -> {
46+
// if (SharedConstants.isDevelopment) this.checkGameData();
47+
// + if (net.minecraftforge.fml.client.ClientModLoader.completeModLoading()) return; // Do not overwrite the error sceen
48+
// + // Show either ConnectScreen or TitleScreen
49+
// }
50+
@Inject(method = "method_18504", at = @At("RETURN"))
51+
private void onResourceReloadComplete(CallbackInfo ci) {
52+
PatchworkClientModLoader.onResourceReloadComplete(!PatchworkClientModLoader.completeModLoading());
53+
}
54+
}

patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServerSubclass.java renamed to patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftDedicatedServer.java

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,27 @@
1717
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818
*/
1919

20-
package net.patchworkmc.mixin.event.lifecycle;
20+
package net.patchworkmc.mixin;
2121

2222
import org.spongepowered.asm.mixin.Mixin;
2323
import org.spongepowered.asm.mixin.injection.At;
24+
import org.spongepowered.asm.mixin.injection.At.Shift;
2425
import org.spongepowered.asm.mixin.injection.Inject;
2526
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
2627

27-
import net.minecraft.server.MinecraftServer;
2828
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
29-
import net.minecraft.server.integrated.IntegratedServer;
3029

31-
import net.patchworkmc.impl.event.lifecycle.LifecycleEvents;
30+
import net.patchworkmc.impl.Patchwork;
3231

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

patchwork-dispatcher/src/main/resources/fabric.mod.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@
1010
"license": "LGPL-2.1-only",
1111
"icon": "assets/patchwork-dispatcher/icon.png",
1212
"environment": "*",
13-
"entrypoints": {
14-
"main": [
15-
"net.patchworkmc.impl.Patchwork"
16-
]
17-
},
1813
"depends": {
1914
"patchwork-api-base": "*",
2015
"patchwork-fml": "*",
2116
"patchwork-registries": "*",
22-
"patchwork-events-lifecycle": "*"
17+
"patchwork-events-lifecycle": "*",
18+
"patchwork-model-loader": "*"
2319
},
20+
"mixins": [
21+
"patchwork-dispatcher.mixins.json"
22+
],
2423
"custom": {
2524
"modmenu:api": true,
2625
"modmenu:parent": "patchwork"

0 commit comments

Comments
 (0)