Skip to content

Commit f132b6e

Browse files
authored
Temp: Fix crash with AE2 GUIs (#156)
* Temporarily replace transformer logic with AE2 ASM * Add PacketBuffer patch * Only use AE2's impl if the mod's loaded
1 parent ba67814 commit f132b6e

File tree

3 files changed

+233
-7
lines changed

3 files changed

+233
-7
lines changed

src/main/java/com/cleanroommc/modularui/core/ClassTransformer.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
package com.cleanroommc.modularui.core;
22

3+
import java.util.function.Consumer;
4+
5+
import com.cleanroommc.modularui.core.temp.ClassSplicer;
36
import com.cleanroommc.modularui.core.visitor.PacketByteBufferVisitor;
47

58
import net.minecraft.launchwrapper.IClassTransformer;
69

710
import org.objectweb.asm.ClassReader;
811
import org.objectweb.asm.ClassWriter;
12+
import org.objectweb.asm.tree.ClassNode;
913

1014
public class ClassTransformer implements IClassTransformer {
1115

1216
@Override
1317
public byte[] transform(String name, String transformedName, byte[] basicClass) {
14-
if (!ModularUICore.stackUpLoaded &&
15-
(transformedName.equals(PacketByteBufferVisitor.PACKET_UTIL_CLASS) ||
16-
(transformedName.equals(PacketByteBufferVisitor.PACKET_BUFFER_CLASS) && !ModularUICore.ae2Loaded))) {
17-
ClassWriter classWriter = new ClassWriter(0);
18-
new ClassReader(basicClass).accept(new PacketByteBufferVisitor(classWriter), 0);
19-
ModularUICore.LOGGER.info("Applied {} ASM from ModularUI", transformedName);
20-
return classWriter.toByteArray();
18+
if (!ModularUICore.stackUpLoaded) {
19+
// Temporarily use AE2's implementation if it's loaded
20+
if (ModularUICore.ae2Loaded && transformedName.equals(PacketByteBufferVisitor.PACKET_UTIL_CLASS)) {
21+
Consumer<ClassNode> consumer = (node) -> {
22+
ClassSplicer.spliceClasses(node, "com.cleanroommc.modularui.core.temp.PacketUtilPatch",
23+
"writeItemStackFromClientToServer");
24+
};
25+
ModularUICore.LOGGER.info("Applied {} ASM, specific for AE2, from ModularUI", transformedName);
26+
return ClassSplicer.processNode(basicClass, consumer);
27+
} else if (transformedName.equals(PacketByteBufferVisitor.PACKET_UTIL_CLASS) ||
28+
transformedName.equals(PacketByteBufferVisitor.PACKET_BUFFER_CLASS)) {
29+
ClassWriter classWriter = new ClassWriter(0);
30+
new ClassReader(basicClass).accept(new PacketByteBufferVisitor(classWriter), 0);
31+
ModularUICore.LOGGER.info("Applied {} ASM from ModularUI", transformedName);
32+
return classWriter.toByteArray();
33+
}
2134
}
2235
return basicClass;
2336
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) 2018, 2020 Adrian Siekierka
3+
*
4+
* This file is part of StackUp.
5+
*
6+
* StackUp is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* StackUp is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with StackUp. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.cleanroommc.modularui.core.temp;
21+
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.util.List;
25+
import java.util.ListIterator;
26+
import java.util.Set;
27+
import java.util.function.Consumer;
28+
29+
import com.cleanroommc.modularui.core.ModularUICore;
30+
31+
import com.google.common.collect.Lists;
32+
import com.google.common.collect.Sets;
33+
import com.google.common.io.ByteStreams;
34+
import org.objectweb.asm.ClassReader;
35+
import org.objectweb.asm.ClassWriter;
36+
import org.objectweb.asm.Opcodes;
37+
import org.objectweb.asm.commons.ClassRemapper;
38+
import org.objectweb.asm.commons.Remapper;
39+
import org.objectweb.asm.tree.AbstractInsnNode;
40+
import org.objectweb.asm.tree.ClassNode;
41+
import org.objectweb.asm.tree.FieldNode;
42+
import org.objectweb.asm.tree.MethodInsnNode;
43+
import org.objectweb.asm.tree.MethodNode;
44+
45+
public class ClassSplicer {
46+
public static byte[] processNode(byte[] data, Consumer<ClassNode> classNodeConsumer) {
47+
ClassReader reader = new ClassReader(data);
48+
ClassNode nodeOrig = new ClassNode();
49+
reader.accept(nodeOrig, 0);
50+
classNodeConsumer.accept(nodeOrig);
51+
ClassWriter writer = new ClassWriter(0);
52+
nodeOrig.accept(writer);
53+
return writer.toByteArray();
54+
}
55+
56+
public static void spliceClasses(final ClassNode data, final String className, final String... methods) {
57+
try (InputStream stream = ModularUICore.class.getClassLoader().getResourceAsStream(className.replace('.', '/') + ".class")) {
58+
spliceClasses(data, ByteStreams.toByteArray(stream), className, methods);
59+
} catch (IOException e) {
60+
throw new RuntimeException(e);
61+
}
62+
}
63+
64+
65+
public static void spliceClasses(final ClassNode nodeData, final byte[] dataSplice, final String className, final String... methods) {
66+
// System.out.println("Splicing from " + className + " to " + targetClassName)
67+
if (dataSplice == null) {
68+
throw new RuntimeException("Class " + className + " not found! This is a ModularUI bug!");
69+
}
70+
71+
final Set<String> methodSet = Sets.newHashSet(methods);
72+
final List<String> methodList = Lists.newArrayList(methods);
73+
74+
final ClassReader readerSplice = new ClassReader(dataSplice);
75+
final String className2 = className.replace('.', '/');
76+
final String targetClassName2 = nodeData.name;
77+
final String targetClassName = targetClassName2.replace('/', '.');
78+
final Remapper remapper = new Remapper() {
79+
public String map(final String name) {
80+
return className2.equals(name) ? targetClassName2 : name;
81+
}
82+
};
83+
84+
ClassNode nodeSplice = new ClassNode();
85+
readerSplice.accept(new ClassRemapper(nodeSplice, remapper), ClassReader.EXPAND_FRAMES);
86+
for (String s : nodeSplice.interfaces) {
87+
if (methodSet.contains(s)) {
88+
nodeData.interfaces.add(s);
89+
System.out.println("Added INTERFACE: " + s);
90+
}
91+
}
92+
93+
for (int i = 0; i < nodeSplice.methods.size(); i++) {
94+
if (methodSet.contains(nodeSplice.methods.get(i).name)) {
95+
MethodNode mn = nodeSplice.methods.get(i);
96+
boolean added = false;
97+
98+
for (int j = 0; j < nodeData.methods.size(); j++) {
99+
if (nodeData.methods.get(j).name.equals(mn.name)
100+
&& nodeData.methods.get(j).desc.equals(mn.desc)) {
101+
MethodNode oldMn = nodeData.methods.get(j);
102+
System.out.println("Spliced in METHOD: " + targetClassName + "." + mn.name);
103+
nodeData.methods.set(j, mn);
104+
if (nodeData.name.equals(nodeSplice.superName)) {
105+
ListIterator<AbstractInsnNode> nodeListIterator = mn.instructions.iterator();
106+
while (nodeListIterator.hasNext()) {
107+
AbstractInsnNode node = nodeListIterator.next();
108+
if (node instanceof MethodInsnNode
109+
&& node.getOpcode() == Opcodes.INVOKESPECIAL) {
110+
MethodInsnNode methodNode = (MethodInsnNode) node;
111+
if (targetClassName2.equals(methodNode.owner)) {
112+
methodNode.owner = nodeData.superName;
113+
}
114+
}
115+
}
116+
}
117+
118+
oldMn.name = methodList.get((methodList.indexOf(oldMn.name)) & (~1)) + "_mui_old";
119+
nodeData.methods.add(oldMn);
120+
added = true;
121+
break;
122+
}
123+
}
124+
125+
if (!added) {
126+
System.out.println("Added METHOD: " + targetClassName + "." + mn.name);
127+
nodeData.methods.add(mn);
128+
added = true;
129+
}
130+
}
131+
}
132+
133+
for (int i = 0; i < nodeSplice.fields.size(); i++) {
134+
if (methodSet.contains(nodeSplice.fields.get(i).name)) {
135+
FieldNode mn = nodeSplice.fields.get(i);
136+
boolean added = false;
137+
138+
for (int j = 0; j < nodeData.fields.size(); j++) {
139+
if (nodeData.fields.get(j).name.equals(mn.name)
140+
&& nodeData.fields.get(j).desc.equals(mn.desc)) {
141+
System.out.println("Spliced in FIELD: " + targetClassName + "." + mn.name);
142+
nodeData.fields.set(j, mn);
143+
added = true;
144+
break;
145+
}
146+
}
147+
148+
if (!added) {
149+
System.out.println("Added FIELD: " + targetClassName + "." + mn.name);
150+
nodeData.fields.add(mn);
151+
added = true;
152+
}
153+
}
154+
}
155+
156+
}
157+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2018, 2020 Adrian Siekierka
3+
*
4+
* This file is part of StackUp.
5+
*
6+
* StackUp is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* StackUp is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with StackUp. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.cleanroommc.modularui.core.temp;
21+
22+
import net.minecraft.item.Item;
23+
import net.minecraft.item.ItemStack;
24+
import net.minecraft.nbt.NBTTagCompound;
25+
import net.minecraft.network.PacketBuffer;
26+
27+
import io.netty.buffer.ByteBuf;
28+
29+
public class PacketUtilPatch extends PacketBuffer {
30+
31+
public PacketUtilPatch(ByteBuf wrapped) {
32+
super(wrapped);
33+
}
34+
35+
public static void writeItemStackFromClientToServer(PacketBuffer buffer, ItemStack stack) {
36+
if (stack.isEmpty()) {
37+
buffer.writeShort(-1);
38+
} else {
39+
buffer.writeShort(Item.getIdFromItem(stack.getItem()));
40+
if (stack.getCount() >= 0 && stack.getCount() <= 64) {
41+
buffer.writeByte(stack.getCount());
42+
} else {
43+
buffer.writeByte(-42);
44+
buffer.writeInt(stack.getCount());
45+
}
46+
buffer.writeShort(stack.getMetadata());
47+
NBTTagCompound tag = null;
48+
49+
if (stack.getItem().isDamageable() || stack.getItem().getShareTag()) {
50+
tag = stack.getTagCompound();
51+
}
52+
53+
buffer.writeCompoundTag(tag);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)