diff --git a/.vscode/settings.json b/.vscode/settings.json index a5eed8b4..dff347b6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "java.dependency.packagePresentation": "hierarchical" + "java.dependency.packagePresentation": "hierarchical", + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable" } \ No newline at end of file diff --git a/src/main/java/com/darkmagician6/eventapi/EventAPI.java b/src/main/java/com/darkmagician6/eventapi/EventAPI.java new file mode 100644 index 00000000..2e763f1b --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/EventAPI.java @@ -0,0 +1,44 @@ +/** + * This is an API used for handling events across your java based projects. + * It's meant to be simple to use without sacrificing performance and extensibility. + * + * Currently the API is in beta phase but it's stable and ready to be used. + * + * If you have any suggestion for improvements/fixes for shit, + * feel free to make a pull request on the bitbucket: https://bitbucket.org/DarkMagician6/eventapi/overview. + * + * For information on how to use the API take a look at the wiki: + * https://bitbucket.org/DarkMagician6/eventapi/wiki/Home + * + * @Todo Improve/update the wiki. + */ +package com.darkmagician6.eventapi; + +/** + * Main class for the API. + * Contains various information about the API. + * + * @author DarkMagician6 + * @since July 31, 2013 + */ +public final class EventAPI { + + /** + * No need to create an Object of this class as all Methods are static. + */ + private EventAPI() { + } + + /** + * The current version of the API. + */ + public static final String VERSION = String.format("%s-%s", "0.7", "beta"); + + /** + * Array containing the authors of the API. + */ + public static final String[] AUTHORS = { + "DarkMagician6" + }; + +} diff --git a/src/main/java/com/darkmagician6/eventapi/EventManager.java b/src/main/java/com/darkmagician6/eventapi/EventManager.java new file mode 100644 index 00000000..d7264757 --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/EventManager.java @@ -0,0 +1,349 @@ +package com.darkmagician6.eventapi; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.darkmagician6.eventapi.events.Event; +import com.darkmagician6.eventapi.events.EventStoppable; +import com.darkmagician6.eventapi.types.Priority; + +/** + * + * + * @author DarkMagician6 + * @since February 2, 2014 + */ +public final class EventManager { + + /** + * HashMap containing all the registered MethodData sorted on the event parameters of the methods. + */ + private static final Map, List> REGISTRY_MAP = new HashMap, List>(); + + /** + * All methods in this class are static so there would be no reason to create an object of the EventManager class. + */ + private EventManager() { + } + + /** + * Registers all the methods marked with the EventTarget annotation in the class of the given Object. + * + * @param object + * Object that you want to register. + */ + public static void register(Object object) { + for (final Method method : object.getClass().getDeclaredMethods()) { + if (isMethodBad(method)) { + continue; + } + + register(method, object); + } + } + + /** + * Registers the methods marked with the EventTarget annotation and that require + * the specified Event as the parameter in the class of the given Object. + * + * @param object + * Object that contains the Method you want to register. + * @param Parameter + * class for the marked method we are looking for. + */ + public static void register(Object object, Class eventClass) { + for (final Method method : object.getClass().getDeclaredMethods()) { + if (isMethodBad(method, eventClass)) { + continue; + } + + register(method, object); + } + } + + /** + * Unregisters all the methods inside the Object that are marked with the EventTarget annotation. + * + * @param object + * Object of which you want to unregister all Methods. + */ + public static void unregister(Object object) { + for (final List dataList : REGISTRY_MAP.values()) { + for (final MethodData data : dataList) { + if (data.getSource().equals(object)) { + dataList.remove(data); + } + } + } + + cleanMap(true); + } + + /** + * Unregisters all the methods in the given Object that have the specified class as a parameter. + * + * @param object + * Object that implements the Listener interface. + * @param Parameter + * class for the method to remove. + */ + public static void unregister(Object object, Class eventClass) { + if (REGISTRY_MAP.containsKey(eventClass)) { + for (final MethodData data : REGISTRY_MAP.get(eventClass)) { + if (data.getSource().equals(object)) { + REGISTRY_MAP.get(eventClass).remove(data); + } + } + + cleanMap(true); + } + } + + /** + * Registers a new MethodData to the HashMap. + * If the HashMap already contains the key of the Method's first argument it will add + * a new MethodData to key's matching list and sorts it based on Priority. @see com.darkmagician6.eventapi.types.Priority + * Otherwise it will put a new entry in the HashMap with a the first argument's class + * and a new CopyOnWriteArrayList containing the new MethodData. + * + * @param method + * Method to register to the HashMap. + * @param object + * Source object of the method. + */ + private static void register(Method method, Object object) { + Class indexClass = (Class) method.getParameterTypes()[0]; + //New MethodData from the Method we are registering. + final MethodData data = new MethodData(object, method, method.getAnnotation(EventTarget.class).value()); + + //Set's the method to accessible so that we can also invoke it if it's protected or private. + if (!data.getTarget().isAccessible()) { + data.getTarget().setAccessible(true); + } + + if (REGISTRY_MAP.containsKey(indexClass)) { + if (!REGISTRY_MAP.get(indexClass).contains(data)) { + REGISTRY_MAP.get(indexClass).add(data); + sortListValue(indexClass); + } + } else { + REGISTRY_MAP.put(indexClass, new CopyOnWriteArrayList() { + //Eclipse was bitching about a serialVersionUID. + private static final long serialVersionUID = 666L; { + add(data); + } + }); + } + } + + /** + * Removes an entry based on the key value in the map. + * + * @param indexClass + * They index key in the map of which the entry should be removed. + */ + public static void removeEntry(Class indexClass) { + Iterator, List>> mapIterator = REGISTRY_MAP.entrySet().iterator(); + + while (mapIterator.hasNext()) { + if (mapIterator.next().getKey().equals(indexClass)) { + mapIterator.remove(); + break; + } + } + } + + /** + * Cleans up the map entries. + * Uses an iterator to make sure that the entry is completely removed. + * + * @param onlyEmptyEntries + * If true only remove the entries with an empty list, otherwise remove all the entries. + */ + public static void cleanMap(boolean onlyEmptyEntries) { + Iterator, List>> mapIterator = REGISTRY_MAP.entrySet().iterator(); + + while (mapIterator.hasNext()) { + if (!onlyEmptyEntries || mapIterator.next().getValue().isEmpty()) { + mapIterator.remove(); + } + } + } + + /** + * Sorts the List that matches the corresponding Event class based on priority value. + * + * @param indexClass + * The Event class index in the HashMap of the List to sort. + */ + private static void sortListValue(Class indexClass) { + List sortedList = new CopyOnWriteArrayList(); + + for (final byte priority : Priority.VALUE_ARRAY) { + for (final MethodData data : REGISTRY_MAP.get(indexClass)) { + if (data.getPriority() == priority) { + sortedList.add(data); + } + } + } + + //Overwriting the existing entry. + REGISTRY_MAP.put(indexClass, sortedList); + } + + /** + * Checks if the method does not meet the requirements to be used to receive event calls from the Dispatcher. + * Performed checks: Checks if the parameter length is not 1 and if the EventTarget annotation is not present. + * + * @param method + * Method to check. + * + * @return True if the method should not be used for receiving event calls from the Dispatcher. + * + * @see com.darkmagician6.eventapi.EventTarget + */ + private static boolean isMethodBad(Method method) { + return method.getParameterTypes().length != 1 || !method.isAnnotationPresent(EventTarget.class); + } + + /** + * Checks if the method does not meet the requirements to be used to receive event calls from the Dispatcher. + * Performed checks: Checks if the parameter class of the method is the same as the event we want to receive. + * + * @param method + * Method to check. + * @param Class + * of the Event we want to find a method for receiving it. + * + * @return True if the method should not be used for receiving event calls from the Dispatcher. + * + * @see com.darkmagician6.eventapi.EventTarget + */ + private static boolean isMethodBad(Method method, Class eventClass) { + return isMethodBad(method) || !method.getParameterTypes()[0].equals(eventClass); + } + + /** + * Call's an event and invokes the right methods that are listening to the event call. + * First get's the matching list from the registry map based on the class of the event. + * Then it checks if the list is not null. After that it will check if the event is an instance of + * EventStoppable and if so it will add an extra check when looping trough the data. + * If the Event was an instance of EventStoppable it will check every loop if the EventStoppable is stopped, and if + * it is it will break the loop, thus stopping the call. + * For every MethodData in the list it will invoke the Data's method with the Event as the argument. + * After that is all done it will return the Event. + * + * @param event + * Event to dispatch. + * + * @return Event in the state after dispatching it. + */ + public static final Event call(final Event event) { + List dataList = REGISTRY_MAP.get(event.getClass()); + + if (dataList != null) { + if (event instanceof EventStoppable) { + EventStoppable stoppable = (EventStoppable) event; + + for (final MethodData data : dataList) { + invoke(data, event); + + if (stoppable.isStopped()) { + break; + } + } + } else { + for (final MethodData data : dataList) { + invoke(data, event); + } + } + } + + return event; + } + + /** + * Invokes a MethodData when an Event call is made. + * + * @param data + * The data of which the targeted Method should be invoked. + * @param argument + * The called Event which should be used as an argument for the targeted Method. + * + * TODO: Error messages. + */ + private static void invoke(MethodData data, Event argument) { + try { + data.getTarget().invoke(data.getSource(), argument); + } catch (IllegalAccessException e) { + } catch (IllegalArgumentException e) { + } catch (InvocationTargetException e) { + } + } + + /** + * + * @author DarkMagician6 + * @since January 2, 2014 + */ + private static final class MethodData { + + private final Object source; + + private final Method target; + + private final byte priority; + + /** + * Sets the values of the data. + * + * @param source + * The source Object of the data. Used by the VM to + * determine to which object it should send the call to. + * @param target + * The targeted Method to which the Event should be send to. + * @param priority + * The priority of this Method. Used by the registry to sort + * the data on. + */ + public MethodData(Object source, Method target, byte priority) { + this.source = source; + this.target = target; + this.priority = priority; + } + + /** + * Gets the source Object of the data. + * + * @return Source Object of the targeted Method. + */ + public Object getSource() { + return source; + } + + /** + * Gets the targeted Method. + * + * @return The Method that is listening to certain Event calls. + */ + public Method getTarget() { + return target; + } + + /** + * Gets the priority value of the targeted Method. + * + * @return The priority value of the targeted Method. + */ + public byte getPriority() { + return priority; + } + + } + +} diff --git a/src/main/java/com/darkmagician6/eventapi/EventTarget.java b/src/main/java/com/darkmagician6/eventapi/EventTarget.java new file mode 100644 index 00000000..48b98dce --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/EventTarget.java @@ -0,0 +1,21 @@ +package com.darkmagician6.eventapi; + +import com.darkmagician6.eventapi.types.Priority; + +import java.lang.annotation.*; + +/** + * Marks a method so that the EventManager knows that it should be registered. + * The priority of the method is also set with this. + * + * @author DarkMagician6 + * @see com.darkmagician6.eventapi.types.Priority + * @since July 30, 2013 + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface EventTarget { + + byte value() default Priority.MEDIUM; +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/Cancellable.java b/src/main/java/com/darkmagician6/eventapi/events/Cancellable.java new file mode 100644 index 00000000..5262a70e --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/Cancellable.java @@ -0,0 +1,26 @@ +package com.darkmagician6.eventapi.events; + +/** + * Simple interface which should be implemented in events that can be cancelled. + * + * @author DarkMagician6 + * @since August 27, 2013 + */ +public interface Cancellable { + + /** + * Gets the current cancelled state of the event. + * + * @return True if the event is cancelled. + */ + boolean isCancelled(); + + /** + * Sets the cancelled state of the event. + * + * @param state + * Whether the event should be cancelled or not. + */ + void setCancelled(boolean state); + +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/Event.java b/src/main/java/com/darkmagician6/eventapi/events/Event.java new file mode 100644 index 00000000..4d2f6ae9 --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/Event.java @@ -0,0 +1,12 @@ +package com.darkmagician6.eventapi.events; + +/** + * The most basic form of an event. + * You have to implement this interface in order for the EventAPI to recognize the event. + * + * @author DarkMagician6 + * @since July 30, 2013 + */ +public interface Event { + +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/EventStoppable.java b/src/main/java/com/darkmagician6/eventapi/events/EventStoppable.java new file mode 100644 index 00000000..e85807ea --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/EventStoppable.java @@ -0,0 +1,38 @@ +package com.darkmagician6.eventapi.events; + +/** + * The most basic form of an stoppable Event. + * Stoppable events are called seperate from other events and the calling of methods is stopped + * as soon as the EventStoppable is stopped. + * + * @author DarkMagician6 + * @since 26-9-13 + */ +public abstract class EventStoppable implements Event { + + private boolean stopped; + + /** + * No need for the constructor to be public. + */ + protected EventStoppable() { + } + + /** + * Sets the stopped state to true. + */ + public void stop() { + stopped = true; + } + + /** + * Checks the stopped boolean. + * + * @return + * True if the EventStoppable is stopped. + */ + public boolean isStopped() { + return stopped; + } + +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/Typed.java b/src/main/java/com/darkmagician6/eventapi/events/Typed.java new file mode 100644 index 00000000..ce7421f5 --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/Typed.java @@ -0,0 +1,24 @@ +package com.darkmagician6.eventapi.events; + + +/** + * Simple interface that should be implemented in typed events. + * A typed event is an event that can be called on multiple places + * with the type defining where it was called. + *

+ * The type should be defined in the constructor when the new instance + * of the event is created. + * + * @author DarkMagician6 + * @since August 27, 2013 + */ +public interface Typed { + + /** + * Gets the current type of the event. + * + * @return The type ID of the event. + */ + byte getType(); + +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/callables/EventCancellable.java b/src/main/java/com/darkmagician6/eventapi/events/callables/EventCancellable.java new file mode 100644 index 00000000..78a1702f --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/callables/EventCancellable.java @@ -0,0 +1,35 @@ +package com.darkmagician6.eventapi.events.callables; + +import com.darkmagician6.eventapi.events.Cancellable; +import com.darkmagician6.eventapi.events.Event; + +/** + * Abstract example implementation of the Cancellable interface. + * + * @author DarkMagician6 + * @since August 27, 2013 + */ +public abstract class EventCancellable implements Event, Cancellable { + + private boolean cancelled; + + protected EventCancellable() { + } + + /** + * @see com.darkmagician6.eventapi.events.Cancellable.isCancelled + */ + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * @see com.darkmagician6.eventapi.events.Cancellable.setCancelled + */ + @Override + public void setCancelled(boolean state) { + cancelled = state; + } + +} diff --git a/src/main/java/com/darkmagician6/eventapi/events/callables/EventTyped.java b/src/main/java/com/darkmagician6/eventapi/events/callables/EventTyped.java new file mode 100644 index 00000000..db7fdad8 --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/events/callables/EventTyped.java @@ -0,0 +1,34 @@ +package com.darkmagician6.eventapi.events.callables; + +import com.darkmagician6.eventapi.events.Event; +import com.darkmagician6.eventapi.events.Typed; + +/** + * Abstract example implementation of the Typed interface. + * + * @author DarkMagician6 + * @since August 27, 2013 + */ +public abstract class EventTyped implements Event, Typed { + + private final byte type; + + /** + * Sets the type of the event when it's called. + * + * @param eventType + * The type ID of the event. + */ + protected EventTyped(byte eventType) { + type = eventType; + } + + /** + * @see com.darkmagician6.eventapi.events.Typed.getType + */ + @Override + public byte getType() { + return type; + } + +} diff --git a/src/main/java/com/darkmagician6/eventapi/types/EventType.java b/src/main/java/com/darkmagician6/eventapi/types/EventType.java new file mode 100644 index 00000000..34fb501b --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/types/EventType.java @@ -0,0 +1,21 @@ +package com.darkmagician6.eventapi.types; + +/** + * Types that can be used for typed events. + * + * @author DarkMagician6 + * @since August 27, 2013 + */ +public class EventType { + + /** + * Used to define the type of a typed event. + */ + public static final byte + PRE = 0, + ON = 1, + POST = 2, + SEND = 3, + RECIEVE = 4; + +} diff --git a/src/main/java/com/darkmagician6/eventapi/types/Priority.java b/src/main/java/com/darkmagician6/eventapi/types/Priority.java new file mode 100644 index 00000000..40bd1274 --- /dev/null +++ b/src/main/java/com/darkmagician6/eventapi/types/Priority.java @@ -0,0 +1,54 @@ +package com.darkmagician6.eventapi.types; + +/** + * The priority for the dispatcher to determine what method should be invoked first. + * Ram was talking about the memory usage of the way I store the data so I decided + * to just use bytes for the priority because they take up only 8 bits of memory + * per value compared to the 32 bits per value of an enum (Same as an integer). + * + * @author DarkMagician6 + * @since August 3, 2013 + */ +public final class Priority { + + public static final byte + /** + * Highest priority, called first. + */ + HIGHEST = 0, + /** + * High priority, called after the highest priority. + */ + HIGH = 1, + /** + * Medium priority, called after the high priority. + */ + MEDIUM = 2, + /** + * Low priority, called after the medium priority. + */ + LOW = 3, + /** + * Lowest priority, called after all the other priorities. + */ + LOWEST = 4; + + /** + * Array containing all the prioriy values. + */ + public static final byte[] VALUE_ARRAY; + + /** + * Sets up the VALUE_ARRAY the first time anything in this class is called. + */ + static { + VALUE_ARRAY = new byte[]{ + HIGHEST, + HIGH, + MEDIUM, + LOW, + LOWEST + }; + } + +} diff --git a/src/main/java/dev/resent/annotation/RenderMod.java b/src/main/java/dev/resent/annotation/RenderMod.java index ff242de0..ddb21691 100644 --- a/src/main/java/dev/resent/annotation/RenderMod.java +++ b/src/main/java/dev/resent/annotation/RenderMod.java @@ -14,5 +14,5 @@ public @interface RenderMod { Category category(); int x(); int y(); - boolean hasSetting(); + boolean hasSetting() default false; } diff --git a/src/main/java/dev/resent/module/base/Mod.java b/src/main/java/dev/resent/module/base/Mod.java index a95bc5fc..6bff1372 100644 --- a/src/main/java/dev/resent/module/base/Mod.java +++ b/src/main/java/dev/resent/module/base/Mod.java @@ -30,7 +30,7 @@ public class Mod { this.name = modInfo.name(); this.category = modInfo.category(); this.hasSetting = modInfo.hasSetting(); - } + } } public Mod(String name, Category cat) { diff --git a/src/main/java/dev/resent/module/base/RenderModule.java b/src/main/java/dev/resent/module/base/RenderModule.java index 6b8b659d..80a55494 100644 --- a/src/main/java/dev/resent/module/base/RenderModule.java +++ b/src/main/java/dev/resent/module/base/RenderModule.java @@ -1,6 +1,7 @@ package dev.resent.module.base; import dev.resent.annotation.RenderMod; +import dev.resent.util.render.RenderUtils; import net.lax1dude.eaglercraft.v1_8.Mouse; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiScreen; @@ -70,21 +71,15 @@ public class RenderModule extends Mod { boolean hovered = mouseX >= getX() && mouseY >= getY() && mouseX < getX() + getWidth() && mouseY < getY() + this.getHeight(); Gui.drawRect(this.x, this.y, this.x + this.getWidth(), this.y + this.getHeight(), hovered ? 0x50FFFFFF : 0x40FFFFFF); - Gui.drawRect(this.x, this.y, this.x + this.getWidth(), this.y + 1, -1); - Gui.drawRect(this.x, this.y, this.x + 1, this.y + getHeight(), -1); - Gui.drawRect(this.x + this.getWidth() - 1, this.y, this.x + getWidth(), this.y + this.getHeight(), -1); - Gui.drawRect(this.x, this.y + this.getHeight() - 1, this.x + getWidth(), this.y + this.getHeight(), -1); + RenderUtils.drawRectOutline(this.x, this.y, this.x+this.getWidth(), this.y+this.getHeight(), -1); boolean mouseOverX = (mouseX >= this.getX() && mouseX <= this.getX() + this.getWidth()); boolean mouseOverY = (mouseY >= this.getY() && mouseY <= this.getY() + this.getHeight()); - if (mouseOverX && mouseOverY) { - if (Mouse.isButtonDown(0)) { - if (!this.dragging) { - this.lastX = x - mouseX; - this.lastY = y - mouseY; - this.dragging = true; - } - } + + if (mouseOverX && mouseOverY && Mouse.isButtonDown(0) && !this.dragging) { + this.lastX = x - mouseX; + this.lastY = y - mouseY; + this.dragging = true; } } diff --git a/src/main/java/dev/resent/module/impl/hud/ArmorHud.java b/src/main/java/dev/resent/module/impl/hud/ArmorHud.java index 723401a8..25d40a7e 100644 --- a/src/main/java/dev/resent/module/impl/hud/ArmorHud.java +++ b/src/main/java/dev/resent/module/impl/hud/ArmorHud.java @@ -1,5 +1,6 @@ package dev.resent.module.impl.hud; +import dev.resent.annotation.RenderMod; import dev.resent.module.base.Category; import dev.resent.module.base.RenderModule; import dev.resent.module.setting.BooleanSetting; @@ -8,14 +9,12 @@ import net.minecraft.client.gui.GuiIngame; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.item.ItemStack; +@RenderMod(name = "ArmorHud", category = Category.HUD, x = 15, y = 4, hasSetting = true) public class ArmorHud extends RenderModule { public ScaledResolution sr; - public ArmorHud() { - super("ArmorHud", Category.HUD, 4, 4, true); - addSetting(helm, chestp, leg, boot, item); - } + public ArmorHud() { addSetting(helm, chestp, leg, boot, item); } public static BooleanSetting helm = new BooleanSetting("Helmet", "", true); public static BooleanSetting chestp = new BooleanSetting("Chestplate", "", true); @@ -23,13 +22,8 @@ public class ArmorHud extends RenderModule { public static BooleanSetting boot = new BooleanSetting("Boots", "", true); public static BooleanSetting item = new BooleanSetting("Item", "", true); - public int getWidth() { - return 20; - } - - public int getHeight() { - return 96; - } + public int getWidth() { return 20; } + public int getHeight() { return 96; } @Override public void draw() {