Created
June 10, 2024 23:47
-
-
Save fallenthereaper/c03caedbd26e8dfa773b309d36679417 to your computer and use it in GitHub Desktop.
Serializable Functional Interfaces
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
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; | |
} | |
} |
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
@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()); | |
} | |
} | |
} |
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
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); | |
} |
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
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