Skip to content

Commit

Permalink
Look in the SERVICE layer for ImmediateWindowProvider implementations (
Browse files Browse the repository at this point in the history
  • Loading branch information
FiniteReality authored Feb 25, 2024
1 parent 9b239e3 commit d492af5
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.fml.loading.ImmediateWindowHandler;
import net.neoforged.fml.loading.ImmediateWindowProvider;
import net.neoforged.fml.loading.progress.StartupNotificationManager;
import net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWImage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ public static List<Path> allExcluded() {

private void scan(final Path gameDirectory) {
try {
locateTransformers("META-INF/services/cpw.mods.modlauncher.api.ITransformationService");
locateTransformers("META-INF/services/net.neoforged.neoforgespi.locating.IModLocator");
for (final String serviceClass : TransformerDiscovererConstants.SERVICES) {
locateTransformers("META-INF/services/" + serviceClass);
}
} catch (IOException e) {
LogManager.getLogger().error("Error during discovery of transform services from the classpath", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@

package net.neoforged.fml.loading;

import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager.Layer;
import net.neoforged.fml.loading.progress.ProgressMeter;
import net.neoforged.fml.loading.progress.StartupNotificationManager;
import net.neoforged.neoforgespi.earlywindow.GraphicsBootstrapper;
import net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand All @@ -23,6 +27,16 @@ public class ImmediateWindowHandler {

private static ProgressMeter earlyProgress;
public static void load(final String launchTarget, final String[] arguments) {
final var layer = Launcher.INSTANCE.findLayerManager()
.flatMap(manager -> manager.getLayer(Layer.SERVICE))
.orElseThrow(() -> new IllegalStateException("Couldn't find SERVICE layer"));
ServiceLoader.load(layer, GraphicsBootstrapper.class)
.stream()
.map(ServiceLoader.Provider::get)
.forEach(bootstrap -> {
LOGGER.debug("Invoking bootstrap method {}", bootstrap.name());
bootstrap.bootstrap(arguments);
});
if (!List.of("forgeclient", "forgeclientuserdev", "forgeclientdev").contains(launchTarget)) {
provider = new DummyProvider();
LOGGER.info("ImmediateWindowProvider not loading because launch target is {}", launchTarget);
Expand All @@ -32,7 +46,7 @@ public static void load(final String launchTarget, final String[] arguments) {
} else {
final var providername = FMLConfig.getConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER);
LOGGER.info("Loading ImmediateWindowProvider {}", providername);
final var maybeProvider = ServiceLoader.load(ImmediateWindowProvider.class)
final var maybeProvider = ServiceLoader.load(layer, ImmediateWindowProvider.class)
.stream()
.map(ServiceLoader.Provider::get)
.filter(p -> Objects.equals(p.name(), providername))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,124 +1,17 @@
/*
* Copyright (c) Forge Development LLC and contributors
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.loading;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;

/**
* This is for allowing the plugging in of alternative early display implementations.
*
* They can be selected through the config value "earlyWindowProvider" which defaults to "fmlearlywindow" implemented by {@link net.neoforged.fml.earlydisplay.DisplayWindow}
*
* There are a few key things to keep in mind if following through on implementation. You cannot access the game state as it
* literally DOES NOT EXIST at the time this object is constructed. You have to be very careful about managing the handoff
* to mojang, be sure that if you're trying to tick your window in a background thread (a nice idea!) that you properly
* transition to the main thread before handoff is complete. Do note that in general, you should construct your GL objects
* on the MAIN thread before starting your ticker, to ensure MacOS compatibility.
* {@inheritDoc}
*
* No doubt many more things can be said here.
* @deprecated This interface exists for binary compatibility. New
* implementations should use {@link net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider}
* instead.
*/
public interface ImmediateWindowProvider {
/**
* @return The name of this window provider. Do NOT use fmlearlywindow.
*/
String name();

/**
* This is called very early on to initialize ourselves. Use this to initialize the window and other GL core resources.
*
* One thing we want to ensure is that we try and create the highest GL_PROFILE we can accomplish.
* GLFW_CONTEXT_VERSION_MAJOR,GLFW_CONTEXT_VERSION_MINOR should be as high as possible on the created window,
* and it should have all the typical profile settings.
*
* @param arguments The arguments provided to the Java process. This is the entire command line, so you can process
* stuff from it.
* @return A runnable that will be periodically ticked by FML during startup ON THE MAIN THREAD. This is usually
* a good place to put glfwPollEvents() tests.
*/
Runnable initialize(String[] arguments);

/**
* This will be called during the handoff to minecraft to update minecraft with the size of the framebuffer we have.
* Generally won't be called because Minecraft figures it out for itself.
* @param width Consumer of the framebuffer width
* @param height Consumer of the framebuffer height
*/
void updateFramebufferSize(IntConsumer width, IntConsumer height);

/**
* This is called to setup the minecraft window, as if Mojang had done it themselves in their Window class. This
* handoff is difficult to get right - you have to make sure that any activities you're doing to the window are finished
* prior to returning. You should try and setup the width and height as Mojang expects - the suppliers give you all that
* information. Alternatively, you can force Mojang to update from the current position of the window in {@link #positionWindow(Optional, IntConsumer, IntConsumer, IntConsumer, IntConsumer)}
* instead. This might give a more seamless experience.
*
* @param width This is the width of the window Mojang expects
* @param height This is the height of the Window Mojang expects.
* @param title This is the title for the window.
* @param monitor This is the monitor it should appear on.
* @return The window id
*/
long setupMinecraftWindow(final IntSupplier width, final IntSupplier height, final Supplier<String> title, final LongSupplier monitor);

/**
* This is called after window handoff to allow us to tell Mojang about our window's position. This might give a
* preferrable user experience to users, because we just tell Mojang our truth, rather than accept theirs.
* @param monitor This is the monitor we're rendering on. Note that this is the Mojang monitor object. You might have trouble unwrapping it.
* @param widthSetter This sets the width on the Mojang side
* @param heightSetter This sets the height on the Mojang side
* @param xSetter This sets the x coordinate on the Mojang side
* @param ySetter This sets the y coordinate on the Mojang side
* @return true if you've handled the window positioning - this skips the "forced fullscreen" code until a later stage
*/
boolean positionWindow(Optional<Object> monitor, IntConsumer widthSetter, IntConsumer heightSetter, IntConsumer xSetter, IntConsumer ySetter);

/**
* Return a Supplier of an object extending the LoadingOverlay class from Mojang. This is what will be used once
* the Mojang window code has taken over rendering of the window, to render the later stages of the loading process.
*
* @param mc This supplies the Minecraft object
* @param ri This supplies the ReloadInstance object that tells us when the loading is finished
* @param ex This Consumes the final state of the loading - if it's an error you pass it the Throwable, otherwise you
* pass Optional.empty()
* @param fade This is the fade flag passed to LoadingOverlay. You probably want to ignore it.
* @param <T> This is the type LoadingOverlay to allow type binding on the Mojang side
* @return A supplier of your later LoadingOverlay screen.
*/
<T> Supplier<T> loadingOverlay(Supplier<?> mc, Supplier<?> ri, Consumer<Optional<Throwable>> ex, boolean fade);

/**
* This is called during the module loading process to allow us to find objects inside the GAME layer, such as a
* later loading screen.
* @param layer This is the GAME layer from ModLauncher
*/
void updateModuleReads(ModuleLayer layer);

/**
* This is called periodically during the loading process to "tick" the window. It is typically the same as the Runnable
* from {@link #initialize(String[])}
*/
void periodicTick();

/**
* This is called to construct a {@link net.neoforged.neoforgespi.locating.ForgeFeature} for the GL_VERSION we
* managed to create for the window. Should be a string of the format {MAJOR}.{MINOR}, such as 4.6, 4.5 or such.
*
* @return the GL profile we created
*/
String getGLVersion();

/**
* This is called during some very early startup routines to show a crash dialog
* using e.g. tinyfd dialogs
* @param message The message to display
*/
void crash(String message);
}
@Deprecated(since = "2.0.16", forRemoval = true)
public interface ImmediateWindowProvider extends net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider
{ }
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Set<String> SERVICES = Set.of(
"cpw.mods.modlauncher.api.ITransformationService",
"net.neoforged.neoforgespi.locating.IModLocator",
"net.neoforged.neoforgespi.locating.IDependencyLocator"
);
private UncheckedIOException alreadyFailed;

@Override
Expand Down Expand Up @@ -102,6 +96,6 @@ private static boolean shouldLoadInServiceLayer(Path path) {
JarMetadata metadata = JarMetadata.from(new JarContentsBuilder().paths(path).build());
return metadata.providers().stream()
.map(SecureJar.Provider::serviceName)
.anyMatch(SERVICES::contains);
.anyMatch(TransformerDiscovererConstants.SERVICES::contains);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.loading;

import cpw.mods.modlauncher.api.IModuleLayerManager.Layer;
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;

import java.util.Set;

/**
* Defines a class containing constants which implementations of {@link ITransformerDiscoveryService}
* may use.
*/
public class TransformerDiscovererConstants {
private TransformerDiscovererConstants() { }

/**
* Defines the set of FML service types which should be loaded on the
* {@link Layer#SERVICE} module layer.
*/
public static final Set<String> SERVICES = Set.of(
"cpw.mods.modlauncher.api.ITransformationService",
"net.neoforged.neoforgespi.locating.IModLocator",
"net.neoforged.neoforgespi.locating.IDependencyLocator",
"net.neoforged.neoforgespi.earlywindow.GraphicsBootstrapper",
"net.neoforged.fml.loading.ImmediateWindowProvider", // FIXME: remove this when removing the legacy ImmediateWindowProvider
"net.neoforged.neoforgespi.earlywindow.ImmediateWindowProvider"
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforgespi.earlywindow;

/**
* Defines a type which can be used to perform any bootstrap operations before
* creating a window during the early loading window process.
*/
public interface GraphicsBootstrapper
{
/**
* The name of this bootstrapper. This is used for logging purposes.
*
* @return The name of this bootstrapper.
*/
String name();

/**
* Performs any bootstrapping that needs to be done before creating a
* window.
*
* @param arguments The arguments provided to the Java process. This is the
* entire command line, so you can process stuff from it.
*/
void bootstrap(String[] arguments);
}
Loading

0 comments on commit d492af5

Please sign in to comment.