-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
966 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package wtf.zani.vanillamenu; | ||
|
||
import org.objectweb.asm.ClassWriter; | ||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.InsnList; | ||
import org.objectweb.asm.tree.MethodNode; | ||
|
||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.function.Predicate; | ||
|
||
public class Util { | ||
public static MethodNode findMethod(ClassNode classNode, Predicate<MethodNode> predicate) { | ||
return classNode.methods | ||
.stream() | ||
.filter(predicate) | ||
.findFirst() | ||
.orElseThrow(); | ||
} | ||
|
||
public static InsnList asm(AbstractInsnNode... nodes) { | ||
final InsnList list = new InsnList(); | ||
|
||
Arrays.stream(nodes).forEach(list::add); | ||
|
||
return list; | ||
} | ||
|
||
public static void dumpClass(ClassNode node) { | ||
try (final FileOutputStream outputStream = new FileOutputStream(node.name.replace('/', '.') + ".class")) { | ||
final ClassWriter classWriter = new ClassWriter(0); | ||
|
||
node.accept(classWriter); | ||
|
||
outputStream.write(classWriter.toByteArray()); | ||
} catch (IOException ignored) { | ||
|
||
} | ||
} | ||
|
||
public static String internalName(Class<?> clazz) { | ||
return clazz.getName().replace('.', '/'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
src/main/java/wtf/zani/vanillamenu/accessors/Accessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package wtf.zani.vanillamenu.accessors; | ||
|
||
import wtf.zani.vanillamenu.accessors.lunar.AccountManagerAccessor; | ||
import wtf.zani.vanillamenu.accessors.lunar.LoadingScreenRendererAccessor; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public abstract class Accessor { | ||
public static AccountManagerAccessor accountAccessor; | ||
public static LoadingScreenRendererAccessor loadingScreenRendererAccessor; | ||
|
||
protected final Map<String, Method> methodCache = new HashMap<>(); | ||
protected final Object wrapped; | ||
|
||
public Accessor(Object wrapped) { | ||
this.wrapped = wrapped; | ||
} | ||
|
||
protected <T> T callSuperMethod(String name, Object... args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { | ||
return this.callMethod(true, name, args); | ||
} | ||
|
||
protected <T> T callMethod(String name, Object... args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { | ||
return this.callMethod(false, name, args); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
protected <T> T callMethod(boolean superMethod, String name, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { | ||
if (this.methodCache.containsKey(name)) { | ||
return (T) this.methodCache.get(name).invoke(this.wrapped, args); | ||
} else { | ||
final Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new); | ||
final Method method = (superMethod ? this.wrapped.getClass().getSuperclass() : this.wrapped.getClass()).getDeclaredMethod(name, argTypes); | ||
|
||
this.methodCache.put(name, method); | ||
|
||
method.setAccessible(true); | ||
|
||
return (T) method.invoke(this.wrapped, args); | ||
} | ||
} | ||
|
||
public Object getWrapped() { | ||
return this.wrapped; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/wtf/zani/vanillamenu/accessors/lunar/AccountAccessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wtf.zani.vanillamenu.accessors.lunar; | ||
|
||
import wtf.zani.vanillamenu.accessors.Accessor; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
|
||
public class AccountAccessor extends Accessor { | ||
public AccountAccessor(Object wrapped) { | ||
super(wrapped); | ||
} | ||
|
||
public String getUsername() { | ||
try { | ||
return this.callSuperMethod("getUsername"); | ||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
src/main/java/wtf/zani/vanillamenu/accessors/lunar/AccountManagerAccessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package wtf.zani.vanillamenu.accessors.lunar; | ||
|
||
import org.objectweb.asm.signature.SignatureReader; | ||
import org.objectweb.asm.signature.SignatureVisitor; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import wtf.zani.vanillamenu.Util; | ||
import wtf.zani.vanillamenu.accessors.Accessor; | ||
import wtf.zani.vanillamenu.hooks.delegations.AccountManagerHook; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.*; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC; | ||
import static org.objectweb.asm.Opcodes.ASM9; | ||
|
||
public class AccountManagerAccessor extends Accessor { | ||
private final Method getAccountsMethod; | ||
private final Method getCurrentAccountMethod; | ||
private final Method setCurrentAccountMethod; | ||
|
||
public AccountManagerAccessor(Object wrapped) throws NoSuchMethodException { | ||
super(wrapped); | ||
|
||
final AtomicReference<String> accountClass = new AtomicReference<>(); | ||
|
||
final ClassNode managerNode = AccountManagerHook.getInstance().node; | ||
final MethodNode getAccountsMethodNode = Util.findMethod(managerNode, methodNode -> { | ||
if ((methodNode.access & ACC_PUBLIC) <= 0 || methodNode.signature == null) { | ||
return false; | ||
} | ||
|
||
final List<String> types = new ArrayList<>(); | ||
|
||
final SignatureVisitor visitor = new SignatureVisitor(ASM9) { | ||
@Override | ||
public void visitClassType(String name) { | ||
types.add(name); | ||
} | ||
}; | ||
|
||
final SignatureReader reader = new SignatureReader(methodNode.signature); | ||
|
||
reader.accept(visitor); | ||
visitor.visitReturnType(); | ||
|
||
accountClass.set(types.get(2)); | ||
|
||
return types.get(0).equals("java/util/Map"); | ||
}); | ||
|
||
this.getAccountsMethod = wrapped.getClass().getMethod(getAccountsMethodNode.name); | ||
this.getCurrentAccountMethod = Arrays.stream(wrapped.getClass().getMethods()) | ||
.filter(method -> | ||
method.getReturnType().getName().replace('.', '/').equals(accountClass.get())) | ||
.findFirst() | ||
.orElseThrow(); | ||
this.setCurrentAccountMethod = Arrays.stream(wrapped.getClass().getMethods()) | ||
.filter(method -> { | ||
if (method.getParameterCount() != 1) return false; | ||
|
||
return method.getReturnType().getName().equals(Void.TYPE.getName()) && method.getParameterTypes()[0].getName().replace('.', '/').equals(accountClass.get()); | ||
}) | ||
.findFirst() | ||
.orElseThrow(); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static void create(Object accountManager) throws NoSuchMethodException { | ||
Accessor.accountAccessor = new AccountManagerAccessor(accountManager); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public Map<String, AccountAccessor> getAccounts() { | ||
try { | ||
final Map<String, Object> accounts = (Map<String, Object>) this.getAccountsMethod.invoke(this.wrapped); | ||
final Map<String, AccountAccessor> mappedAccounts = new HashMap<>(); | ||
|
||
accounts.forEach((uuid, account) -> mappedAccounts.put(uuid, new AccountAccessor(account))); | ||
|
||
return mappedAccounts; | ||
} catch (InvocationTargetException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public AccountAccessor getCurrentAccount() { | ||
try { | ||
final Object account = this.getCurrentAccountMethod.invoke(this.wrapped); | ||
|
||
return account != null ? new AccountAccessor(account) : null; | ||
} catch (InvocationTargetException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public void setAccount(AccountAccessor account) { | ||
try { | ||
this.setCurrentAccountMethod.invoke(this.wrapped, account.getWrapped()); | ||
} catch (InvocationTargetException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/wtf/zani/vanillamenu/accessors/lunar/LoadStageAccessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wtf.zani.vanillamenu.accessors.lunar; | ||
|
||
import wtf.zani.vanillamenu.accessors.Accessor; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
|
||
public class LoadStageAccessor extends Accessor { | ||
public LoadStageAccessor(Object wrapped) { | ||
super(wrapped); | ||
} | ||
|
||
public String getCategory() { | ||
try { | ||
return this.callMethod("getCategory"); | ||
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/wtf/zani/vanillamenu/accessors/lunar/LoadingScreenRendererAccessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package wtf.zani.vanillamenu.accessors.lunar; | ||
|
||
import org.objectweb.asm.signature.SignatureReader; | ||
import org.objectweb.asm.signature.SignatureVisitor; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.FieldNode; | ||
import wtf.zani.vanillamenu.accessors.Accessor; | ||
import wtf.zani.vanillamenu.hooks.delegations.LoadingScreenRendererHook; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import static org.objectweb.asm.Opcodes.ASM9; | ||
|
||
public class LoadingScreenRendererAccessor extends Accessor { | ||
private final Field statusField; | ||
private final Field loadStageField; | ||
|
||
public LoadingScreenRendererAccessor(Object wrapped) throws NoSuchFieldException { | ||
super(wrapped); | ||
|
||
final ClassNode classNode = LoadingScreenRendererHook.getInstance().node; | ||
|
||
this.statusField = Arrays.stream(wrapped.getClass().getDeclaredFields()) | ||
.filter(field -> field.getType().getName().equals(String.class.getName())) | ||
.findFirst() | ||
.orElseThrow(); | ||
this.statusField.trySetAccessible(); | ||
|
||
final FieldNode stageList = classNode.fields.stream() | ||
.filter(fieldNode -> fieldNode.desc.equals("Ljava/util/List;") && fieldNode.signature != null) | ||
.findFirst() | ||
.orElseThrow(); | ||
|
||
final List<String> types = new ArrayList<>(); | ||
|
||
final SignatureVisitor visitor = new SignatureVisitor(ASM9) { | ||
@Override | ||
public void visitClassType(String name) { | ||
types.add(name); | ||
} | ||
}; | ||
|
||
final SignatureReader reader = new SignatureReader(stageList.signature); | ||
|
||
reader.accept(visitor); | ||
visitor.visitEnd(); | ||
|
||
final FieldNode stageFieldNode = classNode.fields.stream() | ||
.filter(fieldNode -> fieldNode.desc.equals("L" + types.get(1) + ";")) | ||
.findFirst() | ||
.orElseThrow(); | ||
|
||
this.loadStageField = wrapped.getClass().getDeclaredField(stageFieldNode.name); | ||
this.loadStageField.trySetAccessible(); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static void create(Object renderer) throws NoSuchFieldException { | ||
if (Accessor.loadingScreenRendererAccessor == null) { | ||
Accessor.loadingScreenRendererAccessor = new LoadingScreenRendererAccessor(renderer); | ||
} | ||
} | ||
|
||
public String getStatus() { | ||
try { | ||
return (String) this.statusField.get(this.wrapped); | ||
} catch (IllegalAccessException exception) { | ||
throw new RuntimeException(exception); | ||
} | ||
} | ||
|
||
public LoadStageAccessor getLoadStage() { | ||
try { | ||
final Object value = this.loadStageField.get(this.wrapped); | ||
|
||
return value != null ? new LoadStageAccessor(value) : null; | ||
} catch (IllegalAccessException exception) { | ||
throw new RuntimeException(exception); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.