Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental support for using Mojmap as a runtime #469

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jetbrains.annotations.Nullable;
import org.quiltmc.loader.api.ModDependency;
import org.quiltmc.loader.api.ModDependencyIdentifier;
import org.quiltmc.loader.api.Version;
Expand Down Expand Up @@ -83,6 +84,7 @@ public class MinecraftGameProvider implements GameProvider {
private String entrypoint;
private Arguments arguments;
private final List<Path> gameJars = new ArrayList<>(2); // env game jar and common game jar, potentially
private Map<String, List<Path>> gameJarsByNamespace = new HashMap<>();
private Path realmsJar;
private final Set<Path> logJars = new HashSet<>();
private boolean log4jAvailable;
Expand All @@ -91,7 +93,6 @@ public class MinecraftGameProvider implements GameProvider {
private McVersion versionData;
private boolean useGameJarForLogging;
private boolean hasModLoader = false;

private final GameTransformer transformer = new GameTransformer(
new EntrypointPatch(this),
new BrandingPatch(),
Expand Down Expand Up @@ -167,8 +168,12 @@ public ModDependencyIdentifier id() {
return Collections.singletonList(new BuiltinMod(paths, metadata.build()));
}

public Path getGameJar() {
return gameJars.get(0);
@Override
public List<Path> getGameJars(@Nullable String namespace) {
if (namespace == null) {
return gameJarsByNamespace.get(QuiltLauncherBase.getLauncher().getTargetNamespace());
}
return gameJarsByNamespace.get(namespace);
}

@Override
Expand All @@ -190,10 +195,6 @@ public boolean isObfuscated() {
return true; // generally yes...
}

@Override
public String getNamespace() {
return QuiltLauncherBase.getLauncher().isDevelopment() ? "named" : "intermediary";
}

@Override
public boolean requiresUrlClassLoader() {
Expand Down Expand Up @@ -384,12 +385,16 @@ public void initialize(QuiltLauncher launcher) {
for (Path obf : obfJars.values()) {
launcher.hideParentPath(obf);
}
if (!launcher.isDevelopment()) {
gameJarsByNamespace.put("official", Collections.unmodifiableList(new ArrayList<>(obfJars.values())));
}

Map<String, Path> newObfJars;
try {
obfJars = GameProviderHelper.deobfuscate(obfJars,
newObfJars = GameProviderHelper.deobfuscate(obfJars,
getGameId(), getNormalizedGameVersion(),
getLaunchDirectory(),
launcher);
launcher, launcher.getTargetNamespace());
} catch (RuntimeException e) {
if ("Unfixable conflicts".equals(e.getMessage())) {
String source = launcher.getMappingConfiguration().getMappingsSource().replace(File.separator, "/");
Expand All @@ -412,13 +417,23 @@ public void initialize(QuiltLauncher launcher) {
}

for (int i = 0; i < gameJars.size(); i++) {
Path newJar = obfJars.get(names[i]);
Path newJar = newObfJars.get(names[i]);
Path oldJar = gameJars.set(i, newJar);

if (logJars.remove(oldJar)) logJars.add(newJar);
}

realmsJar = obfJars.get("realms");
realmsJar = newObfJars.get("realms");
gameJarsByNamespace.put(launcher.getTargetNamespace(), Collections.unmodifiableList(new ArrayList<>(newObfJars.values())));

for (String namespace : launcher.getMappingConfiguration().getNamespaces()) {
if (!namespace.equals("official") && !namespace.equals(launcher.getTargetNamespace())) {
Map<String, Path> output = GameProviderHelper.deobfuscate(obfJars, getGameId(), getNormalizedGameVersion(), getLaunchDirectory(), launcher, namespace);
gameJarsByNamespace.put(namespace, Collections.unmodifiableList(new ArrayList<>(output.values())));
}
}

gameJarsByNamespace = Collections.unmodifiableMap(gameJarsByNamespace);
}

if (!logJars.isEmpty() && !Boolean.getBoolean(SystemProperties.UNIT_TEST)) {
Expand All @@ -433,7 +448,7 @@ public void initialize(QuiltLauncher launcher) {

setupLogHandler(launcher, true);

transformer.locateEntrypoints(launcher, getNamespace(), gameJars);
transformer.locateEntrypoints(launcher, QuiltLauncherBase.getLauncher().getTargetNamespace(), gameJars);
}

private void setupLogHandler(QuiltLauncher launcher, boolean useTargetCl) {
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/org/quiltmc/loader/impl/game/GameProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.Objects;

import net.fabricmc.loader.api.metadata.ModMetadata;

import org.jetbrains.annotations.Nullable;
import org.quiltmc.loader.impl.util.LoaderUtil;
import org.quiltmc.loader.impl.util.QuiltLoaderInternal;
import org.quiltmc.loader.impl.util.QuiltLoaderInternalType;
Expand All @@ -44,9 +46,6 @@ public interface GameProvider {
String getEntrypoint();
Path getLaunchDirectory();
boolean isObfuscated();
default String getNamespace() {
return isObfuscated()? "intermediary": "named";
};
boolean requiresUrlClassLoader();

boolean isEnabled();
Expand All @@ -55,6 +54,14 @@ default String getNamespace() {
GameTransformer getEntrypointTransformer();
void unlockClassPath(QuiltLauncher launcher);
void launch(ClassLoader loader);

/**
* Returns the game jars mapped to the given namespace.
* @param namespace if null, returns the default (mapped) jars
* @return a list of all game jars, or null if they do not exist (or, in development environments,
* are not known to the gameprovider -- see {@link org.quiltmc.loader.impl.util.SystemProperties#REMAP_CLASSPATH_FILE})
*/
@Nullable List<Path> getGameJars(@Nullable String namespace);
default boolean isGameClass(String name) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ public static final class FindResult {

private static boolean emittedInfo = false;

public static Map<String, Path> deobfuscate(Map<String, Path> inputFileMap, String gameId, String gameVersion, Path gameDir, QuiltLauncher launcher) {
public static Map<String, Path> deobfuscate(Map<String, Path> inputFileMap, String gameId, String gameVersion, Path gameDir, QuiltLauncher launcher, String targetNamespace) {
Log.debug(LogCategory.GAME_REMAP, "Requesting deobfuscation of %s", inputFileMap);

if (launcher.isDevelopment()) { // in-dev is already deobfuscated
if (launcher.isDevelopment() && targetNamespace.equals(launcher.getTargetNamespace())) { // in-dev is already deobfuscated
return inputFileMap;
}

Expand All @@ -186,7 +186,6 @@ public static Map<String, Path> deobfuscate(Map<String, Path> inputFileMap, Stri
gameVersion));
}

String targetNamespace = mappingConfig.getTargetNamespace();
MappingTreeView mappings = mappingConfig.getMappings();

if (mappings == null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.format.MappingFormat;

import net.fabricmc.mappingio.format.tiny.Tiny1FileReader;
import net.fabricmc.mappingio.format.tiny.Tiny2FileReader;

import net.fabricmc.mappingio.tree.MappingTree;

import org.quiltmc.loader.api.QuiltLoader;
import org.quiltmc.loader.impl.QuiltLoaderImpl;
import org.quiltmc.loader.impl.game.GameProvider;
import org.quiltmc.loader.impl.util.ManifestUtil;
import org.quiltmc.loader.impl.util.QuiltLoaderInternal;
import org.quiltmc.loader.impl.util.QuiltLoaderInternalType;
Expand All @@ -50,22 +51,22 @@

import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
import net.fabricmc.mappingio.format.proguard.ProGuardFileReader;
import net.fabricmc.mappingio.format.tiny.Tiny1FileReader;
import net.fabricmc.mappingio.tree.MappingTreeView;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.mappingio.tree.VisitableMappingTree;

import org.quiltmc.loader.impl.util.mappings.FilteringMappingVisitor;

@QuiltLoaderInternal(QuiltLoaderInternalType.LEGACY_EXPOSED)
public final class MappingConfiguration {
public class MappingConfiguration {
private boolean initialized;

private String gameId;
private String gameVersion;
private String mappingsSource;
private final VisitableMappingTree mappings = new MemoryMappingTree();
private List<String> namespaces;
private String targetNamespace;

public String getGameId() {
initialize();
Expand Down Expand Up @@ -99,24 +100,23 @@ public MappingTreeView getMappings() {
}

public List<String> getNamespaces() {
initialize();

return namespaces;
}

public String getTargetNamespace() {
GameProvider gameProvider = QuiltLoaderImpl.INSTANCE.tryGetGameProvider();
if (gameProvider != null)
return gameProvider.getNamespace();
// else
// If the game provider doesn't exist yet, use the development flag to set the namespace
return QuiltLauncherBase.getLauncher().isDevelopment() ? "named" : "intermediary";
if (targetNamespace == null) {
targetNamespace = System.getProperty(SystemProperties.TARGET_NAMESPACE, QuiltLauncherBase.getLauncher().isDevelopment() ? "named" : "intermediary");
}
return targetNamespace;
}

public boolean requiresPackageAccessHack() {
// TODO
return getTargetNamespace().equals("named");
}


private void initialize() {
if (initialized) return;

Expand Down Expand Up @@ -150,17 +150,19 @@ private void initialize() {
MappingFormat format = readMappingFormat(reader);
reader.mark(8192*2); // seems to read 2x the buffer size
FilteringMappingVisitor filter = new FilteringMappingVisitor(mappings);
String tinyNamespace = QuiltLauncherBase.getLauncher().isDevelopment() ? "named" : "intermediary";

switch (format) {
case TINY_FILE:
if (!Tiny1FileReader.getNamespaces(reader).contains(getTargetNamespace())) {
if (!Tiny1FileReader.getNamespaces(reader).contains(tinyNamespace)) {
Log.info(LogCategory.MAPPINGS, "Skipping mappings: Missing namespace '%s'", getTargetNamespace());
continue;
}
reader.reset();
Tiny1FileReader.read(reader, filter);
break;
case TINY_2_FILE:
if (!Tiny2FileReader.getNamespaces(reader).contains(getTargetNamespace())) {
if (!Tiny2FileReader.getNamespaces(reader).contains(tinyNamespace)) {
Log.info(LogCategory.MAPPINGS, "Skipping mappings: Missing namespace '%s'", getTargetNamespace());
continue;
}
Expand Down Expand Up @@ -192,6 +194,7 @@ private void initialize() {
// Load mojmap
String mojmapPath = System.getProperty(SystemProperties.MOJMAP_PATH);
if (mojmapPath != null) {
Log.info(LogCategory.MAPPINGS, "Loading mojang mappings: %s", mojmapPath);
try (BufferedReader reader = Files.newBufferedReader(Paths.get(mojmapPath))) {
ProGuardFileReader.read(reader, "mojang", "official", new MappingSourceNsSwitch(mappings, "official"));
} catch (IOException e) {
Expand All @@ -203,6 +206,13 @@ private void initialize() {
namespaces.add(mappings.getSrcNamespace());
namespaces.addAll(mappings.getDstNamespaces());
this.namespaces = Collections.unmodifiableList(namespaces);
Log.info(LogCategory.MAPPINGS, "Loaded mapping namespaces: %s", namespaces);
Log.info(LogCategory.MAPPINGS, "Target namespace: %s", getTargetNamespace());

if (!namespaces.contains(getTargetNamespace())) {
throw new IllegalStateException(String.format("Requested target namespace %s not loaded. Available options: %s", targetNamespace, namespaces));
}

initialized = true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ public interface QuiltLauncher {

String getEntrypoint();

String getTargetNamespace();
default String getTargetNamespace() {
return getMappingConfiguration().getTargetNamespace();
}

List<Path> getClassPath();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.file.Path;
import java.util.Map;

import org.jetbrains.annotations.VisibleForTesting;
import org.quiltmc.loader.impl.FormattedException;
import org.quiltmc.loader.impl.QuiltLoaderImpl;
import org.quiltmc.loader.impl.game.GameProvider;
Expand Down Expand Up @@ -61,7 +62,8 @@ protected static void setProperties(Map<String, Object> propertiesA) {
properties = propertiesA;
}

private static void setLauncher(QuiltLauncher launcherA) {
@VisibleForTesting
public static void setLauncher(QuiltLauncher launcherA) {
if (launcher != null && launcher != launcherA) {
throw new RuntimeException("Duplicate setLauncher call!");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,30 +80,30 @@ public static void init(EnvType side, QuiltLoaderImpl loader) {
throw new IllegalStateException("QuiltMixinBootstrap has already been initialized!");
}

if (QuiltLauncherBase.getLauncher().isDevelopment()) {
MappingConfiguration mappingConfiguration = QuiltLauncherBase.getLauncher().getMappingConfiguration();
MappingTreeView mappings = mappingConfiguration.getMappings();

if (mappings != null) {
List<String> namespaces = new ArrayList<>(mappings.getDstNamespaces());
namespaces.add(mappings.getSrcNamespace());

if (namespaces.contains("intermediary") && namespaces.contains(mappingConfiguration.getTargetNamespace())) {
System.setProperty("mixin.env.remapRefMap", "true");

try {
MixinIntermediaryDevRemapper remapper = new MixinIntermediaryDevRemapper(mappings, "intermediary", mappingConfiguration.getTargetNamespace());
MixinEnvironment.getDefaultEnvironment().getRemappers().add(remapper);
Log.info(LogCategory.MIXIN, "Loaded Quilt development mappings for mixin remapper!");
} catch (Exception e) {
Log.error(LogCategory.MIXIN, "Quilt development environment setup error - the game will probably crash soon!");
e.printStackTrace();
}
MixinBootstrap.init();

MappingConfiguration mappingConfiguration = QuiltLauncherBase.getLauncher().getMappingConfiguration();
MappingTreeView mappings = mappingConfiguration.getMappings();

if (mappings != null) {
List<String> namespaces = new ArrayList<>(mappings.getDstNamespaces());
namespaces.add(mappings.getSrcNamespace());

// TODO: This needs special support for when there's a mod compiled to mojmap
if (namespaces.contains("intermediary") && namespaces.contains(mappingConfiguration.getTargetNamespace()) && !mappingConfiguration.getTargetNamespace().equals("intermediary")) {
System.setProperty("mixin.env.remapRefMap", "true");

try {
MixinIntermediaryDevRemapper remapper = new MixinIntermediaryDevRemapper(mappings, "intermediary", mappingConfiguration.getTargetNamespace());
MixinEnvironment.getDefaultEnvironment().getRemappers().add(remapper);
Log.info(LogCategory.MIXIN, "Loaded Quilt mappings for mixin remapper!");
} catch (Exception e) {
Log.error(LogCategory.MIXIN, "Quilt remap environment setup error - the game will probably crash soon!");
e.printStackTrace();
}
}
}

MixinBootstrap.init();
getMixinConfigs(loader, side).forEach(QuiltMixinBootstrap::addConfiguration);

Map<String, ModContainerExt> configToModMap = new HashMap<>();
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/org/quiltmc/loader/impl/launch/knot/Knot.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ public ClassLoader init(String[] args) {
loader.load();
loader.freeze();

MixinBootstrap.init();
QuiltMixinBootstrap.init(getEnvironmentType(), loader);
QuiltLauncherBase.finishMixinBootstrapping();

Expand Down Expand Up @@ -259,12 +258,6 @@ private static GameProvider findEmbedddedGameProvider() {
}
}

@Override
public String getTargetNamespace() {
// TODO: Won't work outside of Yarn
return isDevelopment ? "named" : "intermediary";
}

@Override
public List<Path> getClassPath() {
return classPath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ private V1ModMetadataImpl readFields(JsonLoaderValue.ObjectImpl root) {
@Nullable
JsonLoaderValue intermediateMappingsValue = quiltLoader.get(QLKeys.INTERMEDIATE_MAPPINGS);

String[] supported_mappings = { "org.quiltmc:hashed", "net.fabricmc:intermediary" };
// TODO: "mojang" for mojmap breaks spec; it is just temporary for now because its experimental
String[] supported_mappings = { "org.quiltmc:hashed", "net.fabricmc:intermediary", "mojang" };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"net.minecraft:mojang"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glitch says this is temporary, but well? I know how permanent placeholders can be like!
Anyway, net.minecraft:mojang makes sense for dev purposes, but Loom uses net.minecraft:mappings, which makes sense but is confusing! (what is Minecraft mappings? I've heard of Mojang!)

So, considering this and Mojang's official standards? Maybe we should go with com.mojang:mappings?

String mappings = "org.quiltmc:hashed";

if (intermediateMappingsValue != null) {
Expand All @@ -309,6 +310,10 @@ private V1ModMetadataImpl readFields(JsonLoaderValue.ObjectImpl root) {
throw new ParseException("Oh no! This version of Quilt Loader doesn't support hashed mappings, please update Quilt Loader to use this mod.");
}

if (mappings.equals("mojang") && !QuiltLoader.getMappingResolver().getNamespaces().contains("mojang")) {
throw new ParseException("Oh no! This version of Quilt Loader doesn't support Mojang mappings, please update Quilt Loader to use this mod.");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This copy-pastey comment doesn't make sense here compared to the Hashed Mojmap one; how about:

Suggested change
throw new ParseException("Oh no! This version of Quilt Loader doesn't support Mojang mappings, please update Quilt Loader to use this mod.");
throw new ParseException("Oh no! This Quilt Loader installation isn't configured to support Mojang mappings.");

(potential remedies to this are to be determined once uhhhh, we figure out a good way to provide mojmap)

}

builder.intermediateMappings = mappings;

// Metadata
Expand Down
Loading
Loading