mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 16:27:47 +00:00
Fix up all the things. Removed SidedProvider because it served no real purpose anymore.
Signed-off-by: cpw <cpw+github@weeksfamily.ca>
This commit is contained in:
20 files changed
+282
-204
No files matched your search
@@ -1,13 +1,18 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.DataPackRegistries;
|
||||
import net.minecraft.resources.IFutureReloadListener;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* The main ResourceManager is recreated on each reload, through {@link DataPackRegistries}'s creation.
|
||||
@@ -30,7 +35,7 @@ public class AddReloadListenerEvent extends Event
|
||||
*/
|
||||
public void addListener(IFutureReloadListener listener)
|
||||
{
|
||||
listeners.add(listener);
|
||||
listeners.add(new WrappedStateAwareListener(listener));
|
||||
}
|
||||
|
||||
public List<IFutureReloadListener> getListeners()
|
||||
@@ -42,4 +47,19 @@ public class AddReloadListenerEvent extends Event
|
||||
{
|
||||
return dataPackRegistries;
|
||||
}
|
||||
private static class WrappedStateAwareListener implements IFutureReloadListener {
|
||||
private final IFutureReloadListener wrapped;
|
||||
|
||||
private WrappedStateAwareListener(final IFutureReloadListener wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> reload(final IStage stage, final IResourceManager resourceManager, final IProfiler preparationsProfiler, final IProfiler reloadProfiler, final Executor backgroundExecutor, final Executor gameExecutor) {
|
||||
if (ModLoader.isLoadingStateValid())
|
||||
return wrapped.reload(stage, resourceManager, preparationsProfiler, reloadProfiler, backgroundExecutor, gameExecutor);
|
||||
else
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import net.minecraft.util.registry.Bootstrap;
|
||||
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
public class DatagenModLoader {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static GatherDataEvent.DataGeneratorConfig dataGeneratorConfig;
|
||||
private static ExistingFileHelper existingFileHelper;
|
||||
private static boolean runningDataGen;
|
||||
|
||||
public static boolean isRunningDataGen() {
|
||||
return runningDataGen;
|
||||
}
|
||||
|
||||
public static void begin(final Set<String> mods, final Path path, final java.util.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;
|
||||
LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
||||
runningDataGen = true;
|
||||
Bootstrap.register();
|
||||
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat);
|
||||
existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
||||
ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
|
||||
ModLoader.get().runEventGenerator(mc->new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper));
|
||||
dataGeneratorConfig.runAll();
|
||||
}
|
||||
}
|
||||
@@ -21,11 +21,7 @@ package net.minecraftforge.fml;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -33,6 +29,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -97,6 +94,20 @@ public class DeferredWorkQueue
|
||||
return CompletableFuture.supplyAsync(work, r->tasks.add(new TaskInfo(modInfo, r)));
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED FOR REMOVAL. Use {@link ParallelDispatchEvent#enqueueWork(Runnable)} or {@link ParallelDispatchEvent#enqueueWork(Supplier)}
|
||||
*
|
||||
* @param workToEnqueue Runnable to execute later
|
||||
* @return a completable future
|
||||
*/
|
||||
@Deprecated
|
||||
public static CompletableFuture<Void> runLater(Runnable workToEnqueue) {
|
||||
return
|
||||
Optional.ofNullable(ModLoadingContext.get().getActiveContainer().modLoadingStage.getDeferredWorkQueue())
|
||||
.map(wq->wq.enqueueWork(ModLoadingContext.get().getActiveContainer().modInfo, workToEnqueue))
|
||||
.orElseGet(()->CompletableFuture.completedFuture(null));
|
||||
}
|
||||
|
||||
static class TaskInfo
|
||||
{
|
||||
public final IModInfo owner;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import net.minecraftforge.fml.loading.StringUtils;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import org.apache.commons.lang3.text.ExtendedMessageFormat;
|
||||
@@ -33,14 +34,22 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.CORE;
|
||||
|
||||
//TODO, this should be re-evaluated now that ITextComponents are passed everywhere instaed of strings.
|
||||
public class ForgeI18n {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
// From FontRenderer.renderCharAtPos
|
||||
private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate();
|
||||
private static Map<String,String> i18n;
|
||||
private static Map<String,FormatFactory> customFactories;
|
||||
// From StringUtils
|
||||
private static final Pattern PATTERN_CONTROL_CODE = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]");
|
||||
|
||||
|
||||
|
||||
static {
|
||||
customFactories = new HashMap<>();
|
||||
@@ -102,6 +111,16 @@ public class ForgeI18n {
|
||||
return extendedMessageFormat.format(args);
|
||||
}
|
||||
|
||||
public static String stripSpecialChars(String message)
|
||||
{
|
||||
// We can't handle many unicode points in the splash renderer
|
||||
return DISALLOWED_CHAR_MATCHER.removeFrom(stripControlCodes(message));
|
||||
}
|
||||
|
||||
public static String stripControlCodes(String text) {
|
||||
return PATTERN_CONTROL_CODE.matcher(text).replaceAll("");
|
||||
}
|
||||
|
||||
public static class CustomReadOnlyFormat extends Format {
|
||||
private final BiConsumer<StringBuffer, Object> formatter;
|
||||
|
||||
|
||||
@@ -112,8 +112,7 @@ public class ModLoader
|
||||
|
||||
private final List<ModLoadingException> loadingExceptions;
|
||||
private final List<ModLoadingWarning> loadingWarnings;
|
||||
private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig;
|
||||
private ExistingFileHelper existingFileHelper;
|
||||
private boolean loadingStateValid;
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
|
||||
|
||||
@@ -162,6 +161,7 @@ public class ModLoader
|
||||
* @param periodicTask Optional periodic task to perform on the main thread while other activities run
|
||||
*/
|
||||
public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
|
||||
loadingStateValid = true;
|
||||
statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete"));
|
||||
FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask);
|
||||
statusConsumer.ifPresent(c->c.accept("Loading mods"));
|
||||
@@ -174,6 +174,7 @@ public class ModLoader
|
||||
if (!this.loadingExceptions.isEmpty()) {
|
||||
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0));
|
||||
modList.setLoadedMods(Collections.emptyList());
|
||||
loadingStateValid = false;
|
||||
throw new LoadingFailedException(loadingExceptions);
|
||||
}
|
||||
statusConsumer.ifPresent(c->c.accept("Building Mod List"));
|
||||
@@ -185,6 +186,7 @@ public class ModLoader
|
||||
if (!loadingExceptions.isEmpty()) {
|
||||
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0));
|
||||
modList.setLoadedMods(Collections.emptyList());
|
||||
loadingStateValid = false;
|
||||
throw new LoadingFailedException(loadingExceptions);
|
||||
}
|
||||
modList.setLoadedMods(modContainers);
|
||||
@@ -225,21 +227,29 @@ public class ModLoader
|
||||
}
|
||||
|
||||
private void dispatchAndHandleError(ModLoadingStage state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker) {
|
||||
if (!isLoadingStateValid()) {
|
||||
LOGGER.error("Cowardly refusing to process mod state change request from {}", state);
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
if (!isLoadingStateValid()) {
|
||||
LOGGER.error("Cowardly refusing to process mod state change request from {}", state);
|
||||
return;
|
||||
}
|
||||
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();
|
||||
syncExecutor.drive(ticker);
|
||||
}
|
||||
try {
|
||||
transition.join();
|
||||
} catch (CompletionException e) {
|
||||
loadingStateValid = false;
|
||||
Throwable t = e.getCause();
|
||||
final List<Throwable> notModLoading = Arrays.stream(t.getSuppressed())
|
||||
.filter(obj -> !(obj instanceof ModLoadingException))
|
||||
@@ -292,7 +302,27 @@ public class ModLoader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the current mod loading state is valid. Use if you interact with vanilla systems directly during loading
|
||||
* and don't want to cause extraneous crashes due to trying to do things that aren't possible in a "broken load"
|
||||
*/
|
||||
public static boolean isLoadingStateValid() {
|
||||
return get().loadingStateValid;
|
||||
}
|
||||
|
||||
public <T extends Event & IModBusEvent> void runEventGenerator(Function<ModContainer, T> generator) {
|
||||
if (!loadingStateValid) {
|
||||
LOGGER.error("Cowardly refusing to send event generator to a broken mod state");
|
||||
return;
|
||||
}
|
||||
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(generator.apply(mc)));
|
||||
}
|
||||
|
||||
public <T extends Event & IModBusEvent> void postEvent(T e) {
|
||||
if (!loadingStateValid) {
|
||||
LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName());
|
||||
return;
|
||||
}
|
||||
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e));
|
||||
}
|
||||
|
||||
@@ -311,20 +341,4 @@ public class ModLoader
|
||||
public static boolean isDataGenRunning () {
|
||||
return runningDataGen;
|
||||
}
|
||||
|
||||
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;
|
||||
// LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
||||
// runningDataGen = true;
|
||||
// Bootstrap.register();
|
||||
// dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat);
|
||||
// existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
||||
// gatherAndInitializeMods(() -> {});
|
||||
// dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {});
|
||||
// dataGeneratorConfig.runAll();
|
||||
}
|
||||
|
||||
public Function<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
|
||||
return mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class ModLoadingContext
|
||||
{
|
||||
private static ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new);
|
||||
private Object languageExtension;
|
||||
private ModLoadingStage stage;
|
||||
|
||||
public static ModLoadingContext get() {
|
||||
return context.get();
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import net.minecraft.util.StringUtils;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -79,4 +81,11 @@ public class ModLoadingException extends RuntimeException
|
||||
public String getMessage() {
|
||||
return formatToString();
|
||||
}
|
||||
|
||||
public IModInfo getModInfo() {
|
||||
return modInfo;
|
||||
}
|
||||
public String getCleanMessage() {
|
||||
return ForgeI18n.stripControlCodes(formatToString());
|
||||
}
|
||||
}
|
||||
@@ -57,14 +57,14 @@ public enum ModLoadingStage
|
||||
ENQUEUE_IMC(InterModEnqueueEvent.class),
|
||||
PROCESS_IMC(InterModProcessEvent.class),
|
||||
COMPLETE(FMLLoadCompleteEvent.class),
|
||||
DONE(),
|
||||
GATHERDATA();
|
||||
DONE();
|
||||
|
||||
private final Supplier<Stream<EventGenerator<?>>> eventFunctionStream;
|
||||
private final EventDispatcher<?> eventManager;
|
||||
private final Optional<Class<? extends ParallelDispatchEvent>> parallelEventClass;
|
||||
private final ThreadSelector threadSelector;
|
||||
private final BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator;
|
||||
private DeferredWorkQueue deferredWorkQueue;
|
||||
|
||||
ModLoadingStage(Class<? extends ParallelDispatchEvent> parallelClass) {
|
||||
final EventGenerator<?> event = EventGenerator.fromFunction(LamdbaExceptionUtils.rethrowFunction((ModContainer mc) -> parallelClass.getConstructor(ModContainer.class).newInstance(mc)));
|
||||
@@ -72,7 +72,7 @@ public enum ModLoadingStage
|
||||
this.threadSelector = ThreadSelector.PARALLEL;
|
||||
this.eventManager = EventDispatcher.identity();
|
||||
this.parallelEventClass = Optional.of(parallelClass);
|
||||
final DeferredWorkQueue deferredWorkQueue = new DeferredWorkQueue(this, parallelClass);
|
||||
deferredWorkQueue = new DeferredWorkQueue(this, parallelClass);
|
||||
this.finalActivityGenerator = (e, prev) -> prev.thenApplyAsync((List<Throwable> t) -> {
|
||||
deferredWorkQueue.runTasks();
|
||||
return t;
|
||||
@@ -145,6 +145,10 @@ public enum ModLoadingStage
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -17,10 +17,10 @@ public class ModWorkManager {
|
||||
boolean selfDriven();
|
||||
boolean driveOne();
|
||||
|
||||
default void drive() {
|
||||
default void drive(Runnable ticker) {
|
||||
if (!selfDriven()) {
|
||||
while (driveOne())
|
||||
;
|
||||
ticker.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +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.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.client.ClientHooks;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public enum SidedProvider
|
||||
{
|
||||
// All of these need to be careful not to directly dereference the client and server elements in their signatures
|
||||
DATAFIXER(
|
||||
c->c.get().getDataFixer(),
|
||||
s->s.get().getDataFixer(),
|
||||
()-> { throw new UnsupportedOperationException(); }),
|
||||
STRIPCHARS(
|
||||
(Function<Supplier<Minecraft>, Function<String, String>>)c-> ClientHooks::stripSpecialChars,
|
||||
s-> str->str,
|
||||
()-> str->str);
|
||||
|
||||
private static Supplier<Minecraft> client;
|
||||
private static Supplier<DedicatedServer> server;
|
||||
|
||||
public static void setClient(Supplier<Minecraft> client)
|
||||
{
|
||||
SidedProvider.client = client;
|
||||
}
|
||||
public static void setServer(Supplier<DedicatedServer> server)
|
||||
{
|
||||
SidedProvider.server = server;
|
||||
}
|
||||
|
||||
private final Function<Supplier<Minecraft>, ?> clientSide;
|
||||
private final Function<Supplier<DedicatedServer>, ?> serverSide;
|
||||
private final Supplier<?> testSide;
|
||||
|
||||
<T> SidedProvider(Function<Supplier<Minecraft>, T> clientSide, Function<Supplier<DedicatedServer>, T> serverSide, Supplier<T> testSide)
|
||||
{
|
||||
this.clientSide = clientSide;
|
||||
this.serverSide = serverSide;
|
||||
this.testSide = testSide;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get() {
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) {
|
||||
return (T)this.clientSide.apply(client);
|
||||
}
|
||||
else if (FMLEnvironment.dist == Dist.DEDICATED_SERVER) {
|
||||
return (T)this.serverSide.apply(server);
|
||||
}
|
||||
else {
|
||||
return (T)this.testSide.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,9 +75,6 @@ public class ClientHooks
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Marker CLIENTHOOKS = MarkerManager.getMarker("CLIENTHOOKS");
|
||||
// From FontRenderer.renderCharAtPos
|
||||
private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate();
|
||||
|
||||
private static final ResourceLocation iconSheet = new ResourceLocation(ForgeVersion.MOD_ID, "textures/gui/icons.png");
|
||||
@Nullable
|
||||
@@ -197,12 +194,6 @@ public class ClientHooks
|
||||
}
|
||||
|
||||
|
||||
public static String stripSpecialChars(String message)
|
||||
{
|
||||
// We can't handle many unicode points in the splash renderer
|
||||
return DISALLOWED_CHAR_MATCHER.removeFrom(net.minecraft.util.StringUtils.stripControlCodes(message));
|
||||
}
|
||||
|
||||
private static SetMultimap<String,ResourceLocation> missingTextures = HashMultimap.create();
|
||||
private static Set<String> badTextureDomains = Sets.newHashSet();
|
||||
private static Table<String, String, Set<ResourceLocation>> brokenTextures = HashBasedTable.create();
|
||||
|
||||
@@ -22,32 +22,40 @@ package net.minecraftforge.fml.client;
|
||||
import static net.minecraftforge.fml.Logging.CORE;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
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.minecraft.util.datafix.codec.DatapackCodec;
|
||||
import net.minecraftforge.fml.BrandingControl;
|
||||
import net.minecraftforge.fml.LoadingFailedException;
|
||||
import net.minecraftforge.fml.LogicalSidedProvider;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
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 net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -80,22 +88,41 @@ public class ClientModLoader
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static boolean loading;
|
||||
private static Minecraft mc;
|
||||
private static boolean loadingComplete;
|
||||
private static LoadingFailedException error;
|
||||
private static EarlyLoaderGUI earlyLoaderGUI;
|
||||
|
||||
private static class SpacedRunnable implements Runnable {
|
||||
static final long NANO_SLEEP_TIME = TimeUnit.MILLISECONDS.toNanos(50);
|
||||
private final Runnable wrapped;
|
||||
private long lastRun;
|
||||
|
||||
private SpacedRunnable(final Runnable wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
this.lastRun = System.nanoTime() - NANO_SLEEP_TIME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (System.nanoTime() - this.lastRun > NANO_SLEEP_TIME) {
|
||||
wrapped.run();
|
||||
this.lastRun = System.nanoTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void begin(final Minecraft minecraft, final ResourcePackList<ClientResourcePackInfo> defaultResourcePacks, final IReloadableResourceManager mcResourceManager, DownloadingPackFinder metadataSerializer)
|
||||
{
|
||||
// force log4j to shutdown logging in a shutdown hook. This is because we disable default shutdown hook so the server properly logs it's shutdown
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown));
|
||||
loading = true;
|
||||
ClientModLoader.mc = minecraft;
|
||||
SidedProvider.setClient(()->minecraft);
|
||||
LogicalSidedProvider.setClient(()->minecraft);
|
||||
LanguageHook.loadForgeAndMCLangs();
|
||||
earlyLoaderGUI = new EarlyLoaderGUI(minecraft.getMainWindow());
|
||||
createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), earlyLoaderGUI::renderTick)).run();
|
||||
createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), new SpacedRunnable(earlyLoaderGUI::renderTick))).run();
|
||||
if (error == null) {
|
||||
ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder);
|
||||
DatapackCodec.field_234880_a_.addModPacks(ResourcePackLoader.getPackNames());
|
||||
mcResourceManager.addReloadListener(ClientModLoader::onResourceReload);
|
||||
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener());
|
||||
ModelLoaderRegistry.init();
|
||||
@@ -110,10 +137,10 @@ public class ClientModLoader
|
||||
|
||||
private static Runnable createRunnableWithCatch(Runnable r) {
|
||||
return ()-> {
|
||||
if (loadingComplete) return;
|
||||
try {
|
||||
r.run();
|
||||
} catch (LoadingFailedException e) {
|
||||
MinecraftForge.EVENT_BUS.shutdown();
|
||||
if (error == null) error = e;
|
||||
}
|
||||
};
|
||||
@@ -121,7 +148,7 @@ public class ClientModLoader
|
||||
|
||||
private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) {
|
||||
earlyLoaderGUI.handleElsewhere();
|
||||
createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, executor -> CompletableFuture.runAsync(ClientModLoader::preSidedRunnable, executor), executor -> CompletableFuture.runAsync(ClientModLoader::postSidedRunnable, executor), ()->{})).run();
|
||||
createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, executor -> CompletableFuture.runAsync(ClientModLoader::preSidedRunnable, executor), executor -> CompletableFuture.runAsync(ClientModLoader::postSidedRunnable, executor), new SpacedRunnable(earlyLoaderGUI::renderTick))).run();
|
||||
}
|
||||
|
||||
private static void postSidedRunnable() {
|
||||
@@ -135,15 +162,15 @@ public class ClientModLoader
|
||||
|
||||
private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor)
|
||||
{
|
||||
createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, ()->{})).run();
|
||||
createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, new SpacedRunnable(earlyLoaderGUI::renderTick))).run();
|
||||
loading = false;
|
||||
loadingComplete = true;
|
||||
// reload game settings on main thread
|
||||
syncExecutor.execute(()->mc.gameSettings.loadOptions());
|
||||
}
|
||||
|
||||
public static VersionChecker.Status checkForUpdates()
|
||||
{
|
||||
|
||||
boolean anyOutdated = ModList.get().getMods().stream()
|
||||
.map(VersionChecker::getResult)
|
||||
.map(result -> result.status)
|
||||
@@ -173,6 +200,24 @@ public class ClientModLoader
|
||||
if (error == null) {
|
||||
// We can finally start the forge eventbus up
|
||||
MinecraftForge.EVENT_BUS.start();
|
||||
} else {
|
||||
final CrashReport crashReport = CrashReport.makeCrashReport(new Exception("Mod Loading has failed"), "Mod loading error has occurred");
|
||||
error.getErrors().forEach(mle -> {
|
||||
final CrashReportCategory category = crashReport.makeCategory(mle.getModInfo().getModId());
|
||||
category.applyStackTrace(mle.getCause());
|
||||
category.addDetail("Failure message", mle.getCleanMessage());
|
||||
category.addDetail("Exception message", mle.getCause().toString());
|
||||
category.addDetail("Mod Version", mle.getModInfo().getVersion().toString());
|
||||
category.addDetail("Mod Issue URL", ((ModFileInfo)mle.getModInfo().getOwningFile()).getConfigElement("issueTrackerURL").orElse("NOT PROVIDED"));
|
||||
});
|
||||
final File file1 = new File(mc.gameDir, "crash-reports");
|
||||
final File file2 = new File(file1, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt");
|
||||
if (crashReport.saveToFile(file2)) {
|
||||
LOGGER.fatal("Crash report saved to {}", crashReport.getFile());
|
||||
} else {
|
||||
LOGGER.fatal("Failed to save crash report");
|
||||
}
|
||||
System.out.print(crashReport.getCompleteReport());
|
||||
}
|
||||
if (error != null || !warnings.isEmpty()) {
|
||||
mc.displayGuiScreen(new LoadingErrorScreen(error, warnings));
|
||||
|
||||
@@ -22,6 +22,7 @@ package net.minecraftforge.fml.event.lifecycle;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -33,19 +34,25 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GatherDataEvent extends ModLifecycleEvent
|
||||
public class GatherDataEvent extends Event implements IModBusEvent
|
||||
{
|
||||
private final DataGenerator dataGenerator;
|
||||
private final DataGeneratorConfig config;
|
||||
private final ExistingFileHelper existingFileHelper;
|
||||
public GatherDataEvent(final ModContainer modContainer, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper)
|
||||
private final ModContainer modContainer;
|
||||
|
||||
public GatherDataEvent(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
super(modContainer);
|
||||
this.modContainer = mc;
|
||||
this.dataGenerator = dataGenerator;
|
||||
this.config = dataGeneratorConfig;
|
||||
this.existingFileHelper = existingFileHelper;
|
||||
}
|
||||
|
||||
public ModContainer getModContainer() {
|
||||
return this.modContainer;
|
||||
}
|
||||
|
||||
public DataGenerator getGenerator() { return this.dataGenerator; }
|
||||
public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; }
|
||||
public boolean includeServer() { return this.config.server; }
|
||||
|
||||
@@ -54,16 +54,16 @@ public class ResourcePackLoader
|
||||
|
||||
public static <T extends ResourcePackInfo> void loadResourcePacks(ResourcePackList<T> resourcePacks, BiFunction<Map<ModFile, ? extends ModFileResourcePack>, BiConsumer<? super ModFileResourcePack, T>, IPackInfoFinder> packFinder) {
|
||||
resourcePackList = resourcePacks;
|
||||
modResourcePacks = ModList.get().getModFiles().stream().
|
||||
filter(mf->!Objects.equals(mf.getModLoader(),"minecraft")).
|
||||
map(mf -> new ModFileResourcePack(mf.getFile())).
|
||||
collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity(), (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new));
|
||||
modResourcePacks = ModList.get().getModFiles().stream()
|
||||
.filter(mf->!Objects.equals(mf.getModLoader(),"minecraft"))
|
||||
.map(mf -> new ModFileResourcePack(mf.getFile()))
|
||||
.collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity(), (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new));
|
||||
resourcePacks.addPackFinder(new LambdaFriendlyPackFinder(packFinder.apply(modResourcePacks, ModFileResourcePack::setPackInfo)));
|
||||
}
|
||||
|
||||
public static List<String> getPackNames()
|
||||
{
|
||||
return modResourcePacks.values().stream().map(pack -> pack.getPackInfo().getName()).collect(Collectors.toList());
|
||||
return ModList.get().applyForEachModFile(mf->"mod:"+mf.getModInfos().get(0).getModId()).filter(n->!n.equals("mod:minecraft")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <V> Comparator<Map.Entry<String,V>> getSorter() {
|
||||
|
||||
@@ -31,14 +31,12 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.resources.IPackNameDecorator;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.world.storage.FolderName;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
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.network.ConnectionType;
|
||||
import net.minecraftforge.fml.network.FMLNetworkConstants;
|
||||
import net.minecraftforge.fml.network.FMLStatusPing;
|
||||
@@ -92,7 +90,6 @@ public class ServerLifecycleHooks
|
||||
currentServer = server;
|
||||
currentServer.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data
|
||||
// on the dedi server we need to force the stuff to setup properly
|
||||
DistExecutor.unsafeRunWhenOn(Dist.DEDICATED_SERVER, ()->()->SidedProvider.setServer(()->(DedicatedServer)server));
|
||||
LogicalSidedProvider.setServer(()->server);
|
||||
ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server));
|
||||
return !MinecraftForge.EVENT_BUS.post(new FMLServerAboutToStartEvent(server));
|
||||
@@ -185,30 +182,6 @@ public class ServerLifecycleHooks
|
||||
|
||||
public static void handleExit(int retVal)
|
||||
{
|
||||
/*
|
||||
CountDownLatch latch = exitLatch;
|
||||
|
||||
if (latch != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
LOGGER.info("Waiting for the server to terminate/save.");
|
||||
if (!latch.await(10, TimeUnit.SECONDS))
|
||||
{
|
||||
LOGGER.warn("The server didn't stop within 10 seconds, exiting anyway.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("Server terminated.");
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warn("Interrupted wait, exiting.");
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
System.exit(retVal);
|
||||
}
|
||||
|
||||
@@ -235,5 +208,4 @@ public class ServerLifecycleHooks
|
||||
consumer.accept(packInfo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,17 +19,23 @@
|
||||
|
||||
package net.minecraftforge.fml.server;
|
||||
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.LoadingFailedException;
|
||||
import net.minecraftforge.fml.LogicalSidedProvider;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.ModLoadingWarning;
|
||||
import net.minecraftforge.fml.ModWorkManager;
|
||||
import net.minecraftforge.fml.SidedProvider;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
|
||||
@@ -39,20 +45,34 @@ public class ServerModLoader
|
||||
private static boolean hasErrors = false;
|
||||
|
||||
public static void load() {
|
||||
SidedProvider.setServer(()-> {
|
||||
throw new IllegalStateException("Unable to access server yet");
|
||||
});
|
||||
LogicalSidedProvider.setServer(()-> {
|
||||
throw new IllegalStateException("Unable to access server yet");
|
||||
});
|
||||
LanguageHook.loadForgeAndMCLangs();
|
||||
try {
|
||||
ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
|
||||
ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), null, null, ()->{});
|
||||
ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), e-> CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e), ()->{});
|
||||
ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{});
|
||||
} catch (LoadingFailedException e) {
|
||||
} catch (LoadingFailedException error) {
|
||||
ServerModLoader.hasErrors = true;
|
||||
throw e;
|
||||
final CrashReport crashReport = CrashReport.makeCrashReport(error, "Mod loading error has occurred");
|
||||
error.getErrors().forEach(mle -> {
|
||||
final CrashReportCategory category = crashReport.makeCategory(mle.getModInfo().getModId());
|
||||
category.applyStackTrace(mle.getCause());
|
||||
category.addDetail("Failure message", mle.getCleanMessage());
|
||||
category.addDetail("Exception message", mle.getCause().toString());
|
||||
category.addDetail("Mod Version", mle.getModInfo().getVersion().toString());
|
||||
category.addDetail("Mod Issue URL", ((ModFileInfo)mle.getModInfo().getOwningFile()).getConfigElement("issueTrackerURL").orElse("NOT PROVIDED"));
|
||||
});
|
||||
final File file1 = new File("crash-reports");
|
||||
final File file2 = new File(file1, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt");
|
||||
if (crashReport.saveToFile(file2)) {
|
||||
LOGGER.fatal("Crash report saved to {}", file2);
|
||||
} else {
|
||||
LOGGER.fatal("Failed to save crash report");
|
||||
}
|
||||
System.out.print(crashReport.getCompleteReport());
|
||||
throw error;
|
||||
}
|
||||
List<ModLoadingWarning> warnings = ModLoader.get().getWarnings();
|
||||
if (!warnings.isEmpty()) {
|
||||
|
||||
Reference in new issue
Block a user