diff options
Diffstat (limited to 'src/main')
8 files changed, 264 insertions, 17 deletions
diff --git a/src/main/java/makamys/neodymium/Compat.java b/src/main/java/makamys/neodymium/Compat.java index 56b5360..35eac6b 100644 --- a/src/main/java/makamys/neodymium/Compat.java +++ b/src/main/java/makamys/neodymium/Compat.java @@ -2,6 +2,8 @@ package makamys.neodymium; import static makamys.neodymium.Neodymium.LOGGER; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.List; import org.lwjgl.opengl.GLContext; @@ -9,6 +11,8 @@ import org.lwjgl.opengl.GLContext; import com.falsepattern.triangulator.api.ToggleableTessellator; import cpw.mods.fml.common.Loader; import makamys.neodymium.util.OFUtil; +import makamys.neodymium.util.virtualjar.IVirtualJar; +import makamys.neodymium.util.virtualjar.VirtualJar; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.Tessellator; @@ -31,10 +35,6 @@ public class Compat { warns.add("Advanced OpenGL is enabled, performance may be poor."); } - if(Loader.isModLoaded("FastCraft") && !OFUtil.isOptiFinePresent()) { - criticalWarns.add("FastCraft is present without OptiFine, this is not supported."); - } - try { Class<?> shaders = Class.forName("shadersmod.client.Shaders"); try { @@ -66,4 +66,32 @@ public class Compat { return changed; } + + public static void forceEnableOptiFineDetectionOfFastCraft() { + if(Compat.class.getResource("/fastcraft/Tweaker.class") != null) { + // If OptiFine is present, it's already on the class path at this point, so our virtual jar won't override it. + LOGGER.info("FastCraft is present, applying hack to forcingly enable FastCraft's OptiFine compat"); + VirtualJar.add(new OptiFineStubVirtualJar()); + } + } + + private static class OptiFineStubVirtualJar implements IVirtualJar { + + @Override + public String getName() { + return "optifine-stub"; + } + + @Override + public InputStream getInputStream(String path) { + if(path.equals("/optifine/OptiFineForgeTweaker.class")) { + // Dummy file to make FastCraft think OptiFine is present. + LOGGER.info("Returning a dummy /optifine/OptiFineForgeTweaker.class to force FastCraft compat."); + return new ByteArrayInputStream(new byte[0]); + } else { + return null; + } + } + + } } diff --git a/src/main/java/makamys/neodymium/Constants.java b/src/main/java/makamys/neodymium/Constants.java new file mode 100644 index 0000000..f8eb12b --- /dev/null +++ b/src/main/java/makamys/neodymium/Constants.java @@ -0,0 +1,13 @@ +package makamys.neodymium; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Constants { + + public static final String MODID = "neodymium"; + public static final Logger LOGGER = LogManager.getLogger(MODID); + + public static final String PROTOCOL = "neodymiumvirtualjar"; + +} diff --git a/src/main/java/makamys/neodymium/MixinConfigPlugin.java b/src/main/java/makamys/neodymium/MixinConfigPlugin.java index 678fadd..df3c76c 100644 --- a/src/main/java/makamys/neodymium/MixinConfigPlugin.java +++ b/src/main/java/makamys/neodymium/MixinConfigPlugin.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Set; import org.spongepowered.asm.lib.tree.ClassNode; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.MixinEnvironment.Phase; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; @@ -17,6 +19,11 @@ public class MixinConfigPlugin implements IMixinConfigPlugin { @Override public void onLoad(String mixinPackage) { Config.reloadConfig(); + + Phase phase = MixinEnvironment.getCurrentEnvironment().getPhase(); + if(phase == Phase.INIT) { + Compat.forceEnableOptiFineDetectionOfFastCraft(); + } } @Override @@ -35,19 +42,22 @@ public class MixinConfigPlugin implements IMixinConfigPlugin { @Override public List<String> getMixins() { List<String> mixins = new ArrayList<>(); - mixins.addAll(Arrays.asList( - "MixinRenderGlobal", - "MixinWorldRenderer", - "MixinTessellator")); - - if (OFUtil.isOptiFinePresent()) { - System.out.println("Detected OptiFine"); - mixins.add("MixinRenderGlobal_OptiFine"); - mixins.add("MixinGameSettings_OptiFine"); - } - - if(Config.replaceOpenGLSplash) { - mixins.add("MixinGuiMainMenu"); + Phase phase = MixinEnvironment.getCurrentEnvironment().getPhase(); + if(phase == Phase.DEFAULT) { + mixins.addAll(Arrays.asList( + "MixinRenderGlobal", + "MixinWorldRenderer", + "MixinTessellator")); + + if (OFUtil.isOptiFinePresent()) { + System.out.println("Detected OptiFine"); + mixins.add("MixinRenderGlobal_OptiFine"); + mixins.add("MixinGameSettings_OptiFine"); + } + + if(Config.replaceOpenGLSplash) { + mixins.add("MixinGuiMainMenu"); + } } return mixins; diff --git a/src/main/java/makamys/neodymium/util/virtualjar/IVirtualJar.java b/src/main/java/makamys/neodymium/util/virtualjar/IVirtualJar.java new file mode 100644 index 0000000..11cef7e --- /dev/null +++ b/src/main/java/makamys/neodymium/util/virtualjar/IVirtualJar.java @@ -0,0 +1,11 @@ +package makamys.neodymium.util.virtualjar; + +import java.io.InputStream; + +public interface IVirtualJar { + + public String getName(); + + public InputStream getInputStream(String path); + +} diff --git a/src/main/java/makamys/neodymium/util/virtualjar/URLStreamHandlerHelper.java b/src/main/java/makamys/neodymium/util/virtualjar/URLStreamHandlerHelper.java new file mode 100644 index 0000000..7a8bef6 --- /dev/null +++ b/src/main/java/makamys/neodymium/util/virtualjar/URLStreamHandlerHelper.java @@ -0,0 +1,49 @@ +// From jimfs, adapted into a helper class +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package makamys.neodymium.util.virtualjar; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.net.URLStreamHandler; + +public class URLStreamHandlerHelper { + + private static final String JAVA_PROTOCOL_HANDLER_PACKAGES = "java.protocol.handler.pkgs"; + + /** + * Generic method that would allow registration of any properly placed {@code Handler} class. + */ + public static void register(Class<? extends URLStreamHandler> handlerClass) { + checkArgument("Handler".equals(handlerClass.getSimpleName())); + + String pkg = handlerClass.getPackage().getName(); + int lastDot = pkg.lastIndexOf('.'); + checkArgument(lastDot > 0, "package for Handler (%s) must have a parent package", pkg); + + String parentPackage = pkg.substring(0, lastDot); + + String packages = System.getProperty(JAVA_PROTOCOL_HANDLER_PACKAGES); + if (packages == null) { + packages = parentPackage; + } else { + packages += "|" + parentPackage; + } + System.setProperty(JAVA_PROTOCOL_HANDLER_PACKAGES, packages); + } + +} diff --git a/src/main/java/makamys/neodymium/util/virtualjar/VirtualJar.java b/src/main/java/makamys/neodymium/util/virtualjar/VirtualJar.java new file mode 100644 index 0000000..8350d6f --- /dev/null +++ b/src/main/java/makamys/neodymium/util/virtualjar/VirtualJar.java @@ -0,0 +1,100 @@ +package makamys.neodymium.util.virtualjar; + +import static makamys.neodymium.Constants.MODID; +import static makamys.neodymium.Constants.PROTOCOL; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.Map; + +import static makamys.neodymium.Constants.LOGGER; + +import makamys.neodymium.util.virtualjar.protocol.neodymiumvirtualjar.Handler; +import net.minecraft.launchwrapper.Launch; + + +public class VirtualJar { + + private static boolean registered; + + private static Map<String, IVirtualJar> jars = new HashMap<>(); + + public static void registerHandler() { + if(registered) return; + + LOGGER.debug("Registering URL protocol handler: " + PROTOCOL); + + // We want the Handler to always be loaded by the same class loader. + Launch.classLoader.addClassLoaderExclusion("makamys." + MODID + ".util.virtualjar.protocol." + PROTOCOL); + + // The Handler is loaded by the AppClassLoader, but it needs to access the state of VirtualJar, which is loaded + // by the LaunchClassLoader. The solution? Make the Handler just a proxy that delegates the real work to + // VirtualJar.StreamHandlerImpl. We use the blackboard as a class loader-agnostic way of sharing information. + Handler.IURLStreamHandlerImpl streamHandlerImpl = new StreamHandlerImpl(); + Launch.blackboard.put(MODID + "." + PROTOCOL + ".impl", streamHandlerImpl); + URLStreamHandlerHelper.register(Handler.class); + + registered = true; + } + + public static void add(IVirtualJar jar) { + registerHandler(); + + LOGGER.trace("Adding virtual jar to class path: " + PROTOCOL + ":" + jar.getName() + ".jar"); + + String urlStr = PROTOCOL + ":" + jar.getName() + ".jar!/"; + + try { + URL url = new URL(urlStr); + Launch.classLoader.addURL(url); + // Forge expects all URLs in the sources list to be convertible to File objects, so we must remove it to + // avoid a crash. + Launch.classLoader.getSources().remove(url); + + jars.put(jar.getName(), jar); + } catch(MalformedURLException e) { + LOGGER.fatal("Failed to add virtual jar to class path"); + e.printStackTrace(); + } + } + + public static class StreamHandlerImpl implements Handler.IURLStreamHandlerImpl { + + @Override + public URLConnection openConnection(URL url) { + return new URLConnection(url) { + public void connect() { + + } + + public Object getContent() throws IOException { + return super.getContent(); + } + + public String getHeaderField(String name) { + return super.getHeaderField(name); + } + + public InputStream getInputStream() { + String path = getURL().getPath(); + String nameSuffix = ".jar!"; + int nameEnd = path.indexOf(nameSuffix); + String name = path.substring(0, nameEnd); + + IVirtualJar jar = jars.get(name); + + return jar.getInputStream(path.substring(nameEnd + nameSuffix.length())); + } + + public java.io.OutputStream getOutputStream() throws IOException { + return super.getOutputStream(); + } + }; + } + } + +} diff --git a/src/main/java/makamys/neodymium/util/virtualjar/protocol/neodymiumvirtualjar/Handler.java b/src/main/java/makamys/neodymium/util/virtualjar/protocol/neodymiumvirtualjar/Handler.java new file mode 100644 index 0000000..9f0fe66 --- /dev/null +++ b/src/main/java/makamys/neodymium/util/virtualjar/protocol/neodymiumvirtualjar/Handler.java @@ -0,0 +1,24 @@ +package makamys.neodymium.util.virtualjar.protocol.neodymiumvirtualjar; + +import static makamys.neodymium.Constants.MODID; +import static makamys.neodymium.Constants.PROTOCOL; + +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import net.minecraft.launchwrapper.Launch; + +public class Handler extends URLStreamHandler { + + private static final IURLStreamHandlerImpl impl = (IURLStreamHandlerImpl)Launch.blackboard.get(MODID + "." + PROTOCOL + ".impl"); + + @Override + protected URLConnection openConnection(URL url) { + return impl.openConnection(url); + } + + public interface IURLStreamHandlerImpl { + URLConnection openConnection(URL url); + } +} diff --git a/src/main/resources/neodymium-init.mixin.json b/src/main/resources/neodymium-init.mixin.json new file mode 100644 index 0000000..f524b65 --- /dev/null +++ b/src/main/resources/neodymium-init.mixin.json @@ -0,0 +1,12 @@ +{ + "required": true, + "minVersion": "0.6", + "package": "makamys.neodymium.mixin", + "refmap": "neodymium.mixin.refmap.json", + "compatibilityLevel": "JAVA_8", + "mixins": [ + + ], + "plugin": "makamys.neodymium.MixinConfigPlugin", + "target": "@env(INIT)" +} |