Skip to content

Commit dded48c

Browse files
authored
Rework the interface between NeoForge and the FML early loading screen (#260)
1 parent 856c8c0 commit dded48c

File tree

6 files changed

+93
-263
lines changed

6 files changed

+93
-263
lines changed

earlydisplay/src/main/java/net/neoforged/fml/earlydisplay/DisplayWindow.java

Lines changed: 21 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
import java.awt.Desktop;
1313
import java.io.IOException;
14-
import java.lang.reflect.Method;
15-
import java.lang.reflect.Modifier;
1614
import java.net.URI;
1715
import java.nio.ByteBuffer;
1816
import java.nio.file.Files;
@@ -22,7 +20,6 @@
2220
import java.util.Calendar;
2321
import java.util.List;
2422
import java.util.Objects;
25-
import java.util.Optional;
2623
import java.util.StringJoiner;
2724
import java.util.concurrent.ExecutionException;
2825
import java.util.concurrent.Executors;
@@ -34,18 +31,12 @@
3431
import java.util.concurrent.atomic.AtomicBoolean;
3532
import java.util.concurrent.locks.ReentrantLock;
3633
import java.util.function.BiConsumer;
37-
import java.util.function.Consumer;
38-
import java.util.function.Function;
39-
import java.util.function.IntConsumer;
40-
import java.util.function.IntSupplier;
41-
import java.util.function.LongSupplier;
42-
import java.util.function.Supplier;
4334
import java.util.stream.Collector;
4435
import java.util.stream.Collectors;
4536
import joptsimple.OptionParser;
4637
import net.neoforged.fml.loading.FMLConfig;
4738
import net.neoforged.fml.loading.FMLPaths;
48-
import net.neoforged.fml.loading.ImmediateWindowHandler;
39+
import net.neoforged.fml.loading.progress.ProgressMeter;
4940
import net.neoforged.fml.loading.progress.StartupNotificationManager;
5041
import net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider;
5142
import org.jetbrains.annotations.Nullable;
@@ -76,6 +67,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
7667
private static final int[][] GL_VERSIONS = new int[][] { { 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 }, { 3, 3 }, { 3, 2 } };
7768
private static final Logger LOGGER = LoggerFactory.getLogger("EARLYDISPLAY");
7869
private final AtomicBoolean animationTimerTrigger = new AtomicBoolean(true);
70+
private final ProgressMeter mainProgress;
7971

8072
private ColourScheme colourScheme;
8173
private ElementShader elementShader;
@@ -103,10 +95,13 @@ public class DisplayWindow implements ImmediateWindowProvider {
10395

10496
private final Semaphore renderLock = new Semaphore(1);
10597
private boolean maximized;
106-
private String glVersion;
10798
private SimpleFont font;
10899
private Runnable repaintTick = () -> {};
109100

101+
public DisplayWindow() {
102+
mainProgress = StartupNotificationManager.addProgressBar("EARLY", 0);
103+
}
104+
110105
@Override
111106
public String name() {
112107
return "fmlearlywindow";
@@ -270,15 +265,14 @@ void paintFramebuffer() {
270265
}
271266

272267
// Called from NeoForge
273-
public void render(int alpha) {
268+
public void renderToFramebuffer() {
274269
GlDebug.pushGroup("update EarlyDisplay framebuffer");
275270
GlState.readFromOpenGL();
276271
var backup = GlState.createSnapshot();
277272

278273
GlState.viewport(0, 0, this.context.scaledWidth(), this.context.scaledHeight());
279-
RenderElement.globalAlpha = alpha;
280274
framebuffer.activate();
281-
GlState.clearColor(colourScheme.background().redf(), colourScheme.background().greenf(), colourScheme.background().bluef(), alpha / 255f);
275+
GlState.clearColor(colourScheme.background().redf(), colourScheme.background().greenf(), colourScheme.background().bluef(), 1f);
282276
elementShader.activate();
283277
elementShader.updateScreenSizeUniform(this.context.scaledWidth(), this.context.scaledHeight());
284278
paintFramebuffer();
@@ -305,11 +299,6 @@ public Runnable start(@Nullable String mcVersion, final String forgeVersion) {
305299

306300
private static final String ERROR_URL = "https://links.neoforged.net/early-display-errors";
307301

308-
@Override
309-
public String getGLVersion() {
310-
return this.glVersion;
311-
}
312-
313302
private final ReentrantLock crashLock = new ReentrantLock();
314303

315304
private void crashElegantly(String errorDetails) {
@@ -445,7 +434,6 @@ public void initWindow(@Nullable String mcVersion) {
445434
var min = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
446435
var gotVersion = maj + "." + min;
447436
LOGGER.info("Requested GL version " + requestedVersion + " got version " + gotVersion);
448-
this.glVersion = gotVersion;
449437
this.window = window;
450438

451439
int[] x = new int[1];
@@ -539,7 +527,7 @@ private void handleLastGLFWError(BiConsumer<Integer, String> handler) {
539527
*
540528
* @return the Window we own.
541529
*/
542-
public long setupMinecraftWindow(final IntSupplier width, final IntSupplier height, final Supplier<String> title, final LongSupplier monitorSupplier) {
530+
public long takeOverGlfwWindow() {
543531
// wait for the window to actually be initialized
544532
try {
545533
this.initializationFuture.get(30, TimeUnit.SECONDS);
@@ -550,7 +538,7 @@ public long setupMinecraftWindow(final IntSupplier width, final IntSupplier heig
550538
crashElegantly("We seem to be having trouble initializing the window, waited for 30 seconds");
551539
}
552540
// we have to spin wait for the window ticker
553-
ImmediateWindowHandler.updateProgress("Initializing Game Graphics");
541+
updateProgress("Initializing Game Graphics");
554542
while (!this.windowTick.isDone()) {
555543
this.windowTick.cancel(false);
556544
}
@@ -572,7 +560,6 @@ public long setupMinecraftWindow(final IntSupplier width, final IntSupplier heig
572560

573561
glfwMakeContextCurrent(window);
574562
// Set the title to what the game wants
575-
glfwSetWindowTitle(window, title.get());
576563
glfwSwapInterval(0);
577564
// Clean up our hooks
578565
glfwSetFramebufferSizeCallback(window, null).free();
@@ -584,40 +571,7 @@ public long setupMinecraftWindow(final IntSupplier width, final IntSupplier heig
584571
}
585572

586573
@Override
587-
public boolean positionWindow(final Optional<Object> monitor, final IntConsumer widthSetter, final IntConsumer heightSetter, final IntConsumer xSetter, final IntConsumer ySetter) {
588-
widthSetter.accept(this.winWidth);
589-
heightSetter.accept(this.winHeight);
590-
xSetter.accept(this.winX);
591-
ySetter.accept(this.winY);
592-
return true;
593-
}
594-
595-
@Override
596-
public void updateFramebufferSize(final IntConsumer width, final IntConsumer height) {
597-
width.accept(this.fbWidth);
598-
height.accept(this.fbHeight);
599-
}
600-
601-
private Method loadingOverlay;
602-
603-
@SuppressWarnings("unchecked")
604-
@Override
605-
public <T> Supplier<T> loadingOverlay(final Supplier<?> mc, final Supplier<?> ri, final Consumer<Optional<Throwable>> ex, final boolean fade) {
606-
try {
607-
return (Supplier<T>) loadingOverlay.invoke(null, mc, ri, ex, this);
608-
} catch (Throwable e) {
609-
throw new IllegalStateException("How did you get here?", e);
610-
}
611-
}
612-
613-
@Override
614-
public void updateModuleReads(final ModuleLayer layer) {
615-
var fm = layer.findModule("neoforge").orElseThrow();
616-
getClass().getModule().addReads(fm);
617-
var clz = Class.forName(fm, "net.neoforged.neoforge.client.loading.NeoForgeLoadingOverlay");
618-
var methods = Arrays.stream(clz.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).collect(Collectors.toMap(Method::getName, Function.identity()));
619-
loadingOverlay = methods.get("newInstance");
620-
}
574+
public void updateModuleReads(final ModuleLayer layer) {}
621575

622576
public int getFramebufferTextureId() {
623577
return framebuffer.getTexture();
@@ -633,6 +587,16 @@ public void periodicTick() {
633587
repaintTick.run();
634588
}
635589

590+
@Override
591+
public void updateProgress(String label) {
592+
mainProgress.label(label);
593+
}
594+
595+
@Override
596+
public void completeProgress() {
597+
mainProgress.complete();
598+
}
599+
636600
public void addMojangTexture(final int textureId) {
637601
this.elements.add(0, RenderElement.mojang(textureId, framecount));
638602
// this.elements.get(0).retire(framecount + 1);

loader/src/main/java/net/neoforged/fml/ModLoader.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import net.neoforged.fml.i18n.FMLTranslations;
3535
import net.neoforged.fml.loading.FMLEnvironment;
3636
import net.neoforged.fml.loading.FMLLoader;
37-
import net.neoforged.fml.loading.ImmediateWindowHandler;
3837
import net.neoforged.fml.loading.LoadingModList;
3938
import net.neoforged.fml.loading.moddiscovery.ModFileInfo;
4039
import net.neoforged.fml.loading.moddiscovery.ModInfo;
@@ -96,7 +95,6 @@ public static void gatherAndInitializeMods(final Executor syncExecutor, final Ex
9695
loadingIssues.addAll(loadingModList.getModLoadingIssues());
9796

9897
ForgeFeature.registerFeature("javaVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.BOTH, System.getProperty("java.version")));
99-
ForgeFeature.registerFeature("openGLVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.CLIENT, ImmediateWindowHandler.getGLVersion()));
10098
FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask);
10199
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).toList(),
102100
loadingModList.getMods());
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) NeoForged and contributors
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*/
5+
6+
package net.neoforged.fml.loading;
7+
8+
import org.jetbrains.annotations.Nullable;
9+
10+
/**
11+
* Interface for use by NeoForge to control the early loading screen.
12+
*/
13+
public interface EarlyLoadingScreenController {
14+
/**
15+
* Gets the current loading screen controller.
16+
*/
17+
@Nullable
18+
static EarlyLoadingScreenController current() {
19+
return ImmediateWindowHandler.provider;
20+
}
21+
22+
/**
23+
* Takes over ownership of the GLFW window created by the early loading screen.
24+
* <p>
25+
* This method can only be called once and once this method is called, any off-thread
26+
* interaction with the window seizes.
27+
*
28+
* @return The GLFW window handle for the window in a state that can be used by the game.
29+
*/
30+
long takeOverGlfwWindow();
31+
32+
/**
33+
* After calling {@linkplain #takeOverGlfwWindow() taking over} the main window, the game may still want to
34+
* periodically ask the loading screen to update itself independently. It will call this method to do so.
35+
*/
36+
void periodicTick();
37+
38+
/**
39+
* Sets a label for the main progress bar on the early loading screen.
40+
*/
41+
void updateProgress(String label);
42+
43+
/**
44+
* Clears all current progress in preparation for drawing a Minecraft overlay on top of the loading
45+
* screen.
46+
*/
47+
void completeProgress();
48+
}

0 commit comments

Comments
 (0)