Skip to content

Instantly share code, notes, and snippets.

@fallenthereaper
Created June 10, 2024 23:47
Show Gist options
  • Save fallenthereaper/c03caedbd26e8dfa773b309d36679417 to your computer and use it in GitHub Desktop.
Save fallenthereaper/c03caedbd26e8dfa773b309d36679417 to your computer and use it in GitHub Desktop.
Serializable Functional Interfaces
package com.fallenreaper.insight.core.api.core.reflection;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
//10/06/2024
public class ClasspathScanner implements ClassScanner {
private static final Map<String, List<Class<?>>> CACHE = new HashMap<>();
private final List<ClassFilter> filters = new ArrayList<>();
// public static String getIdentifier(Class<?> clazz) {
// IdentifiableInterface annotation = clazz.getAnnotation(IdentifiableInterface.class);
// if (annotation != null) {
// return annotation.value();
// }
// return null;
// }
public ClasspathScanner addFilter(ClassFilter filter) {
filters.add(filter);
return this;
}
@Override
public List<Class<?>> scan(String packageName) {
if (CACHE.containsKey(packageName)) {
return CACHE.get(packageName);
}
List<Class<?>> classes = new ArrayList<>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
CACHE.put(packageName, classes);
return classes;
}
private List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
if (files == null) {
return classes;
}
for (File file : files) {
if (file.isDirectory()) {
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
Class<?> clazz = Class.forName(className);
if (filters.stream().allMatch(filter -> filter.accept(clazz))) {
classes.add(clazz);
}
}
}
return classes;
}
}
@WorkInProgress
public abstract class DynamicPacket<R extends GenericInterface<?>> extends BidirectionalEventPacket {
private Class<R> packetClass;
public DynamicPacket(FriendlyByteBuf buffer) {
super(buffer);
}
public DynamicPacket(R generic, @Nullable PacketData<?>... sourceData) {
super(null, -1, sourceData);
this.packetClass = (Class<R>) generic.getClass();
InsightAPI.INTERFACE_REF.registerHandler(generic.getClass(), generic);
}
@Override
public void decode(FriendlyByteBuf buffer) {
try {
this.packetClass = (Class<R>) Class.forName(buffer.readUtf());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
int size = buffer.readInt();
this.readData = new Object[size];
if (size > 0) {
SerializerFactory.BuiltInFactory factory = SerializerRegistry.getFactory();
for (int i = 0; i < size; i++) {
boolean flag = buffer.readBoolean();
int serializerId = buffer.readInt();
if (serializerId != -1) {
DataSerializer<?> serializer = null;
if (flag) {
for (Integer enumId : factory.getEnumSerializers().inverse().values()) {
if (enumId == serializerId) {
serializer = factory.getEnumSerializer(enumId);
break;
}
}
} else {
serializer = factory.getSerializer(serializerId);
}
if (serializer != null) {
Object fromBuffer = serializer.readFromBuffer(buffer);
readData[i] = fromBuffer;
}
}
}
}
}
@Override
public void encode(FriendlyByteBuf buffer) {
buffer.writeUtf(packetClass.getName());
PacketData<?>[] sourceData = this.getSourceData();
if (sourceData == null) {
buffer.writeInt(0); // Prevent for null objects
} else {
boolean enumFlag = false;
SerializerFactory.BuiltInFactory builtInFactory = SerializerRegistry.getFactory();
buffer.writeInt(sourceData.length);
for (PacketData<?> object : sourceData) {
if (object != null) {
DataSerializer<?> serializer = object.getSerializer();
int serializerId;
if (EnumSerializer.class.isAssignableFrom(serializer.getClass())) {
enumFlag = true;
serializerId = builtInFactory.getEnumSerializerId(((EnumSerializer<?>) serializer));
} else {
serializerId = builtInFactory.getSerializerId(serializer);
}
buffer.writeBoolean(enumFlag);
buffer.writeInt(serializerId);
object.encode(buffer);
} else {
buffer.writeInt(-1);
}
}
}
}
public abstract void compile(Method method, Object... data);
@Override
protected void onHandle(Identifiable holderKey, Level level, Player player) {
super.onHandle(holderKey, level, player);
Method handler = InsightAPI.INTERFACE_REF.getHandler(packetClass);
if (handler != null) {
// Object invoke = handler.invoke(handler.getDeclaringClass().newInstance(), getReadData());
this.compile(handler, getReadData());
} else {
throw new IllegalArgumentException("No handler found for packet: " + packetClass.getName());
}
}
}
package com.fallenreaper.insight.core.api.core.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
//10/06/2024
@IdentifiableInterface
@FunctionalInterface
public interface GenericInterface<T> {
static <T> Method getMethod(GenericInterface<T> handler) {
try {
return handler.getClass().getMethod("apply", Object[].class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to retrieve the method", e);
}
}
static <T> Method getMethod(Class<?> handlerClass) {
return Arrays.stream(handlerClass.getMethods())
.filter(method -> method.getName().equals("apply"))
.findFirst()
.orElseThrow(() -> new RuntimeException("No method named 'apply' found"));
}
default Method asMethod() {
return getMethod(this);
}
default T handle(Object... params) {
try {
Method method = asMethod();
return (T) method.invoke(this, params);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to invoke method", e);
}
}
T apply(Object... params);
}
package com.fallenreaper.insight.core.api.core.reflection;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
//10/06/2024
public class InterfaceRegistry {
private final Map<Class<?>, Method> handlers = new HashMap<>();
private final ClasspathScanner scanner = new ClasspathScanner();
private Predicate<Class<?>> cachingPolicy = clazz -> true; // Default: cache all
public InterfaceRegistry policy(Predicate<Class<?>> policy) {
this.cachingPolicy = policy;
return this;
}
public void registerHandler(Class<?> packetClass, GenericInterface<?> generic) {
Method method = generic.asMethod();
registerHandler(packetClass, method);
}
public void registerHandler(Class<?> packetClass, Method method) {
handlers.put(packetClass, method);
}
public ClasspathScanner getScanner() {
return scanner;
}
public Method getHandler(Class<?> packetClass) {
return handlers.get(packetClass);
}
//NOT WORKING
public void scanAndRegisterHandlers(String packageName) {
scanner.addFilter(clazz -> clazz.isAnnotationPresent(IdentifiableInterface.class) ||
GenericInterface.class.isAssignableFrom(clazz));
List<Class<?>> handlerClasses = scanner.scan(packageName);
for (Class<?> handlerClass : handlerClasses) {
if (cachingPolicy.test(handlerClass)) {
try {
Method method = handlerClass.getMethod("apply", Object[].class);
registerHandler(handlerClass, method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment