Skip to content

Commit

Permalink
Try recover more lambdas Optifine changes
Browse files Browse the repository at this point in the history
Specifically aimed at the keyboard handling ones
Fixes modmuss50#238
Fixes modmuss50#244
  • Loading branch information
Chocohead committed Jun 12, 2021
1 parent 94bb9c3 commit 9a00c42
Show file tree
Hide file tree
Showing 13 changed files with 581 additions and 51 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ loader_version=0.10.8
fabric_version=0.31.0+1.16
fabric_asm_version=v2.2

mod_version = 1.11.1
mod_version = 1.11.2
maven_group = me.modmuss50
archives_base_name = optifabric
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package me.modmuss50.optifabric.compat.architectury.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.Window;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;

import me.modmuss50.optifabric.compat.InterceptingMixin;
import me.modmuss50.optifabric.compat.PlacatingSurrogate;
import me.modmuss50.optifabric.compat.Shim;

@Mixin(GameRenderer.class)
@InterceptingMixin("dev/architectury/mixin/fabric/client/MixinGameRenderer")
abstract class GameRendererNewMixin {
@Shim
private native void renderScreenPre(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, MatrixStack matrices);

@PlacatingSurrogate
private void renderScreenPre(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window) {
}

@Inject(method = "render(FJZ)V", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true,
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", ordinal = 0))
private void renderScreenPre(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack matrices) {
renderScreenPre(tickDelta, startTime, tick, call, mouseX, mouseY, matrices);
}

@Shim
private native void renderScreenPost(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, MatrixStack matrices);

@PlacatingSurrogate
private void renderScreenPost(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window) {
}

@Inject(method = "render(FJZ)V", locals = LocalCapture.CAPTURE_FAILHARD,
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", shift = Shift.AFTER, ordinal = 0))
private void renderScreenPost(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack matrices) {
renderScreenPost(tickDelta, startTime, tick, call, mouseX, mouseY, matrices);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.modmuss50.optifabric.compat.architectury.mixin;

import java.util.Set;
import java.util.stream.Stream;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.SpriteAtlasTexture.Data;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.profiler.Profiler;

import me.modmuss50.optifabric.compat.InterceptingMixin;
import me.modmuss50.optifabric.compat.PlacatingSurrogate;
import me.modmuss50.optifabric.compat.Shim;

@Mixin(SpriteAtlasTexture.class)
@InterceptingMixin("dev/architectury/mixin/fabric/client/MixinTextureAtlas")
abstract class SpriteAtlasTextureNewMixin {
@PlacatingSurrogate
private void preStitch(ResourceManager resourceManager, Stream<Identifier> idStream, Profiler profiler, int mipmapLevel, CallbackInfoReturnable<Data> call, int mipmapLevels) {
}

@Inject(method = "stitch", locals = LocalCapture.CAPTURE_FAILHARD,
at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = "ldc=extracting_frames"))
private void preStitch(ResourceManager resourceManager, Stream<Identifier> idStream, Profiler profiler, int mipmapLevel, CallbackInfoReturnable<Data> call, int mipmapLevels, Set<Identifier> set) {
preStitch(resourceManager, idStream, profiler, mipmapLevels, call, set);
}

@Shim
private native void preStitch(ResourceManager resourceManager, Stream<Identifier> idStream, Profiler profiler, int mipmapLevel, CallbackInfoReturnable<Data> call, Set<Identifier> set);
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,10 @@ public boolean getAsBoolean() {
Mixins.addConfiguration("optifabric.compat.images.mixins.json");
}

if (isPresent("architectury", ">=1.0.20")) {
if (isPresent("architectury", ">=2.0")) {
assert isPresent("minecraft", ">=1.17-beta.1");
Mixins.addConfiguration("optifabric.compat.architectury-AB.new-mixins.json");
} else if (isPresent("architectury", ">=1.0.20")) {
Mixins.addConfiguration("optifabric.compat.architectury-B.mixins.json");
} else if (isPresent("architectury", ">=1.0.4")) {
Mixins.addConfiguration("optifabric.compat.architectury-A.mixins.json");
Expand Down
37 changes: 22 additions & 15 deletions src/main/java/me/modmuss50/optifabric/mod/OptifineSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@

import me.modmuss50.optifabric.mod.OptifineVersion.JarType;
import me.modmuss50.optifabric.patcher.ClassCache;
import me.modmuss50.optifabric.patcher.LambdaRebuiler;
import me.modmuss50.optifabric.patcher.LambdaRebuilder;
import me.modmuss50.optifabric.patcher.StaticFuzzer;
import me.modmuss50.optifabric.util.ThrowingFunction;
import me.modmuss50.optifabric.util.ZipUtils;

public class OptifineSetup {
Expand Down Expand Up @@ -72,6 +74,11 @@ public static Pair<File, ClassCache> getRuntime() throws IOException {
//Validate that the classCache found is for the same input jar
if (Arrays.equals(classCache.getHash(), modHash)) {
System.out.println("Found existing patched optifine jar, using that");

if (classCache.isConverted()) {
classCache.save(optifinePatches);
}

return Pair.of(remappedJar, classCache);
} else {
System.out.println("Class cache is from a different optifine jar, deleting and re-generating");
Expand Down Expand Up @@ -126,20 +133,16 @@ public static Pair<File, ClassCache> getRuntime() throws IOException {
}, jarOfTheFree);

System.out.println("Finding lambdas to fix");
LambdaRebuiler rebuilder = new LambdaRebuiler(jarOfTheFree, minecraftJar.toFile());
LambdaRebuilder rebuilder = new LambdaRebuilder(jarOfTheFree, minecraftJar.toFile());
rebuilder.buildLambdaMap();

System.out.println("Remapping optifine with fixed lambda names");
File lambdaFixJar = new File(versionDir, "Optifine-lambdafix.jar");
Path[] libraries = getLibs(minecraftJar);
remapOptifine(jarOfTheFree, libraries, lambdaFixJar, rebuilder);
RetainingMappingsProvider lambdas = new RetainingMappingsProvider();
Map<String, Map<String, String>> fuzzes = rebuilder.load("official", lambdas);

String namespace = FabricLoader.getInstance().getMappingResolver().getCurrentRuntimeNamespace();
System.out.println("Remapping optifine from official to " + namespace);
remapOptifine(lambdaFixJar, libraries, remappedJar, createMappings("official", namespace));
remapOptifine(jarOfTheFree, getLibs(minecraftJar), remappedJar, createMappings("official", namespace, lambdas));

//We are done, lets get rid of the stuff we no longer need
lambdaFixJar.delete();
jarOfTheFree.delete();
if (OptifineVersion.jarType == JarType.OPTIFINE_INSTALLER) {
optifineModJar.delete();
Expand All @@ -155,7 +158,9 @@ public static Pair<File, ClassCache> getRuntime() throws IOException {
ZipUtils.extract(remappedJar, optifineClasses);
}

return Pair.of(remappedJar, generateClassCache(remappedJar, optifinePatches, modHash, extract));
try (StaticFuzzer transformer = !fuzzes.isEmpty() ? new StaticFuzzer(fuzzes) : null) {
return Pair.of(remappedJar, generateClassCache(remappedJar, fuzzes.isEmpty() ? IOUtils::toByteArray : transformer, optifinePatches, modHash, extract));
}
}

private static void runInstaller(File installer, File output, File minecraftJar) throws IOException {
Expand All @@ -177,9 +182,9 @@ private static void remapOptifine(File input, Path[] libraries, File output, IMa
private static void remapOptifine(Path input, Path[] libraries, Path output, IMappingProvider mappings) throws IOException {
Files.deleteIfExists(output);

TinyRemapper remapper = TinyRemapper.newRemapper().withMappings(mappings).renameInvalidLocals(FabricLoader.getInstance().isDevelopmentEnvironment()).rebuildSourceFilenames(true).build();
TinyRemapper remapper = TinyRemapper.newRemapper().withMappings(mappings).skipLocalVariableMapping(true).renameInvalidLocals(FabricLoader.getInstance().isDevelopmentEnvironment()).rebuildSourceFilenames(true).build();

try (OutputConsumerPath outputConsumer = new Builder(output).build()) {
try (OutputConsumerPath outputConsumer = new Builder(output).assumeArchive(true).build()) {
outputConsumer.addNonClassFiles(input);
remapper.readInputs(input);
remapper.readClassPath(libraries);
Expand All @@ -193,7 +198,7 @@ private static void remapOptifine(Path input, Path[] libraries, Path output, IMa
}

//Optifine currently has two fields that match the same name as Yarn mappings, we'll rename Optifine's to something else
private static IMappingProvider createMappings(String from, String to) {
private static IMappingProvider createMappings(String from, String to, IMappingProvider extra) {
TinyTree normalMappings = FabricLauncherBase.getLauncher().getMappingConfiguration().getMappings();

Map<String, ClassDef> nameToClass = normalMappings.getClasses().stream().collect(Collectors.toMap(clazz -> clazz.getName("intermediary"), Function.identity()));
Expand Down Expand Up @@ -229,6 +234,8 @@ private static IMappingProvider createMappings(String from, String to) {
TinyRemapperMappingsHelper.create(normalMappings, from, to).load(out);

extraFields.forEach(out::acceptField);

extra.load(out);
};
}

Expand Down Expand Up @@ -301,7 +308,7 @@ private static Path getLaunchMinecraftJar() {
return contextJars.get(0);
}

private static ClassCache generateClassCache(File from, File to, byte[] hash, boolean extractClasses) throws IOException {
private static ClassCache generateClassCache(File from, ThrowingFunction<InputStream, byte[], IOException> transformer, File to, byte[] hash, boolean extractClasses) throws IOException {
File classesDir = new File(to.getParent(), "classes");
if (extractClasses) {
if (classesDir.exists()) {
Expand All @@ -317,7 +324,7 @@ private static ClassCache generateClassCache(File from, File to, byte[] hash, bo

if ((name.startsWith("net/minecraft/") || name.startsWith("com/mojang/")) && name.endsWith(".class")) {
try (InputStream in = jarFile.getInputStream(entry)) {
byte[] bytes = IOUtils.toByteArray(in);
byte[] bytes = transformer.apply(in);

classCache.addClass(name.substring(0, name.length() - 6), bytes);
if (extractClasses) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package me.modmuss50.optifabric.mod;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import net.fabricmc.tinyremapper.IMappingProvider;
import net.fabricmc.tinyremapper.IMappingProvider.MappingAcceptor;

public class RetainingMappingsProvider implements MappingAcceptor, IMappingProvider {
private static class LocalMapping {
final Member method;
final int lvtIndex;
final int start;
final int asmIndex;
final String name;

public LocalMapping(Member method, int lvtIndex, int start, int asmIndex, String name) {
this.method = method;
this.lvtIndex = lvtIndex;
this.start = start;
this.asmIndex = asmIndex;
this.name = name;
}
}
private final List<Pair<String, String>> classes = new ArrayList<>();
private final List<Pair<Member, String>> methods = new ArrayList<>();
private final List<Triple<Member, Integer, String>> parameters = new ArrayList<>();
private final List<LocalMapping> locals = new ArrayList<>();
private final List<Pair<Member, String>> fields = new ArrayList<>();

@Override
public void acceptClass(String srcName, String dstName) {
classes.add(Pair.of(srcName, dstName));
}

@Override
public void acceptMethod(Member method, String name) {
methods.add(Pair.of(method, name));
}

@Override
public void acceptMethodArg(Member method, int lvtIndex, String name) {
parameters.add(Triple.of(method, lvtIndex, name));
}

@Override
public void acceptMethodVar(Member method, int lvtIndex, int start, int asmIndex, String name) {
locals.add(new LocalMapping(method, lvtIndex, start, asmIndex, name));
}

@Override
public void acceptField(Member field, String name) {
fields.add(Pair.of(field, name));
}

@Override
public void load(MappingAcceptor out) {
take(classes, out::acceptClass);
take(methods, out::acceptMethod);
take(fields, out::acceptField);

for (Triple<Member, Integer, String> triple : parameters) {
out.acceptMethodArg(triple.getLeft(), triple.getMiddle(), triple.getRight());
}
parameters.clear();

for (LocalMapping mapping : locals) {
out.acceptMethodVar(mapping.method, mapping.lvtIndex, mapping.start, mapping.asmIndex, mapping.name);
}
locals.clear();
}

private <T, U> void take(List<Pair<T, U>> things, BiConsumer<T, U> acceptor) {
for (Pair<T, U> pair : things) {
acceptor.accept(pair.getLeft(), pair.getRight());
}
things.clear();
}
}
20 changes: 18 additions & 2 deletions src/main/java/me/modmuss50/optifabric/patcher/ClassCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
public class ClassCache {
private final byte[] hash;
private final Map<String, byte[]> classes = new HashMap<>();
private boolean converted;

public ClassCache(byte[] hash) {
this.hash = hash;
Expand Down Expand Up @@ -79,7 +80,8 @@ public static ClassCache read(File input) throws IOException {
char formatRevision = dis.readChar(); //Check the format of the file
boolean isFormatA = formatRevision == 'A';
boolean isFormatB = formatRevision == 'B';
if (!isFormatA && !isFormatB && formatRevision != 'C') return new ClassCache(null);
boolean isFormatC = formatRevision == 'C';
if (!isFormatA && !isFormatB && !isFormatC && formatRevision != 'D') return new ClassCache(null);

long expectedCRC = dis.readLong();

Expand Down Expand Up @@ -139,6 +141,16 @@ public FieldVisitor visitField(int access, String name, String descriptor, Strin
}
}

if (isFormatA || isFormatB || isFormatC) {
try (StaticFuzzer fuzzer = new StaticFuzzer()) {
for (Entry<String, byte[]> entry : classCache.classes.entrySet()) {
entry.setValue(fuzzer.apply(entry.getValue()));
}
}

classCache.converted = true;
}

return classCache;
}
}
Expand All @@ -149,7 +161,7 @@ public void save(File output) throws IOException {
}

try (DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(output)))) {
dos.writeChar('C'); //Format version
dos.writeChar('D'); //Format version
dos.writeLong(calculateCRC()); //Expected CRC to get from fully reading

//Write the hash
Expand All @@ -173,4 +185,8 @@ public void save(File output) throws IOException {
}
}
}

public boolean isConverted() {
return converted;
}
}
Loading

0 comments on commit 9a00c42

Please sign in to comment.