-
Notifications
You must be signed in to change notification settings - Fork 29
/
FMLModContainer.java
158 lines (141 loc) · 7.11 KB
/
FMLModContainer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.javafmlmod;
import net.minecraftforge.eventbus.EventBusErrorMessage;
import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.IEventListener;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingException;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.event.IModBusEvent;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class FMLModContainer extends ModContainer
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker LOADING = MarkerManager.getMarker("LOADING");
private final ModFileScanData scanResults;
private final IEventBus eventBus;
private Object modInstance;
private final Class<?> modClass;
public FMLModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer)
{
super(info);
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {}", className);
this.scanResults = modFileScanResults;
activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
this.contextExtension = () -> contextExtension;
try
{
var layer = gameLayer.findModule(info.getOwningFile().moduleName()).orElseThrow();
modClass = Class.forName(layer, className);
LOGGER.trace(LOADING,"Loaded modclass {} with {}", modClass.getName(), modClass.getClassLoader());
}
catch (Throwable e)
{
LOGGER.error(LOADING, "Failed to load class {}", className, e);
throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e);
}
}
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable)
{
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
}
private void constructMod()
{
try
{
LOGGER.trace(LOADING, "Loading mod instance {} of type {}", getModId(), modClass.getName());
try {
// Try noargs constructor first
this.modInstance = modClass.getDeclaredConstructor().newInstance();
} catch (NoSuchMethodException ignored) {
// Otherwise look for constructor that can accept more arguments
Map<Class<?>, Object> allowedConstructorArgs = Map.of(
IEventBus.class, eventBus,
ModContainer.class, this,
FMLModContainer.class, this);
constructorsLoop: for (var constructor : modClass.getDeclaredConstructors()) {
var parameterTypes = constructor.getParameterTypes();
Object[] constructorArgs = new Object[parameterTypes.length];
Set<Class<?>> foundArgs = new HashSet<>();
for (int i = 0; i < parameterTypes.length; i++) {
Object argInstance = allowedConstructorArgs.get(parameterTypes[i]);
if (argInstance == null) {
// Unknown argument, try next constructor method...
continue constructorsLoop;
}
if (foundArgs.contains(parameterTypes[i])) {
throw new RuntimeException("Duplicate constructor argument type: " + parameterTypes[i]);
}
foundArgs.add(parameterTypes[i]);
constructorArgs[i] = argInstance;
}
// All arguments are found
this.modInstance = constructor.newInstance(constructorArgs);
}
if (this.modInstance == null) {
throw new RuntimeException("Could not find mod constructor. Allowed optional argument classes: " +
allowedConstructorArgs.keySet().stream().map(Class::getSimpleName).collect(Collectors.joining(", ")));
}
}
LOGGER.trace(LOADING, "Loaded mod instance {} of type {}", getModId(), modClass.getName());
}
catch (Throwable e)
{
if (e instanceof InvocationTargetException) e = e.getCause(); // exceptions thrown when a reflected method call throws are wrapped in an InvocationTargetException. However, this isn't useful for the end user who has to dig through the logs to find the actual cause.
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
}
try {
LOGGER.trace(LOADING, "Injecting Automatic event subscribers for {}", getModId());
AutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.getClassLoader());
LOGGER.trace(LOADING, "Completed Automatic event subscribers for {}", getModId());
} catch (Throwable e) {
LOGGER.error(LOADING,"Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
}
}
@Override
public boolean matches(Object mod)
{
return mod == modInstance;
}
@Override
public Object getMod()
{
return modInstance;
}
public IEventBus getEventBus()
{
return this.eventBus;
}
@Override
protected <T extends Event & IModBusEvent> void acceptEvent(final T e) {
try {
LOGGER.trace(LOADING, "Firing event for modid {} : {}", this.getModId(), e);
this.eventBus.post(e);
LOGGER.trace(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);
}
}
}