JOSH KING

projects | back


Revision

What?
Revision is a hacked Minecraft client that I wrote and developed for about a year. It went through numerous name changes until I finally settled on 'Revision'. It features a fully working event dispatch system for implementing code into the runtime as well as a debug system that maintains any errors that occur and 'ping' them to the user.



Event Dispatch System
Due to the way Minecraft was made, making a lightweight event dispatch system was essential in keeping the client running efficently as well as being able to sideload modules/plugins into the client. Due to the restrictions within Minecraft I created an event system that I named 'stmyhell'.

/*
 * EventHandler
 *
 * 8/23/2013
 *
 */

package [REDACTED].revisionlib.event;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Processes the firing of events to registered listeners
 * @author [REDACTED]
 * @version 1
 * @since 8/23/2013 3:00 PM
 */
public class EventHandler {

    /**
     * List of ListenerInstances used for firing events
     */
    private List'<'ListenerInstance'>' listeners = new ArrayList'<'ListenerInstance'>'();

    /**
     * Registers a listening class and all of its listening methods.
     * @param listener Listening class
     */
    public void registerListener(EventListener listener) {
        ListenerInstance listenerInstance = new ListenerInstance(listener);
        this.listeners.add(listenerInstance);
        this.registerMethods(listenerInstance);
    }

    /**
     * Registers a listening class and all of its listening methods.
     * @param listener ListenerInstance
     */
    private void registerMethods(ListenerInstance listener) {
        for (Method method : listener.listener.getClass().getDeclaredMethods()) {
            for (Annotation annotation : method.getDeclaredAnnotations()) {
                if (annotation instanceof HandleEvent) {
                    listener.methods.add(method);
                }
            }
        }
    }

    /**
     * Sends an event to all listening methods with a given priority
     * @param event Target event
     * @param priority Target priority
     */
    private void sendEventsToPriority(Event event, EventPriority priority){
        for(ListenerInstance listenerInstance : this.listeners){
            for (Method method : listenerInstance.methods) {
                //Stop any cancelled events
                if(event instanceof Cancellable){
                    Cancellable cancellable = (Cancellable)event;
                    if(cancellable.getCancelled()){
                        return;
                    }
                }
                if(method.getAnnotation(HandleEvent.class).priority() != priority)continue;
                for (Class'<'?'>' c : method.getParameterTypes()) {
                    if (event.getClass().isAssignableFrom(c)) {
                        try {
                            method.invoke(listenerInstance.listener, event);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * Sends an event to all of the listeners in order of priority
     * @param event Target event
     */
    public void fireEvent(Event event){
        for(EventPriority priority : EventPriority.values()){
            this.sendEventsToPriority(event, priority);
        }
    }
}


Module Manager
I created a module manager that finds modules using reflection and loads them into runtime. Using reflection means that I didn't have to specify what modules need to be loaded as well as making it very easy for the user to create their own modules and then implement them into Revision.

/*
 * ModuleManager
 *
 * 8/23/13
 */

package [REDACTED];

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import [REDACTED].revisionlib.reflection.ReflectionHelper;

/**
 * Provides management for modules
 * @author [REDACTED]
 * @version 1
 * @since 8/23/13 6:17 PM
 */
public abstract class ModuleManager {

    /**
     * Loads classes and creates module objects from an internal package
     * @param targetPackage The package location with folders 
     * separated with periods
     */
    public Module[] createModuleInstancesFromPackage(String targetPackage) {
        List'<'Module'>' moduleList = new ArrayList'<'Module'>'();
        for(Class indexClass : ReflectionHelper.getClassesInPackage(targetPackage)) {
            try {
                Module module = (Module)indexClass.newInstance();
                moduleList.add(module);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassCastException e) {
                e.printStackTrace();
            }
        }
        return moduleList.toArray(new Module[moduleList.size()]);
    }
    
    /**
     * Loads classes and creates module objects from an external jar
     * @param packageDirectory The package location with folders
     * separated with periods
     * @param jarFile The target jar file to create modules from
     */
    public Module[] createModuleInstancesFromPackageInJar(File jarFile) {
        List'<'Module'>' moduleList = new ArrayList'<'Module'>'();
        
        Class[] jarClassList = ReflectionHelper.getClassesFromExternalJar(jarFile);
        
        if (jarClassList != null) { 
	        for(Class indexClass : jarClassList) {
	            try {
	                Module module = (Module)indexClass.newInstance();
	                moduleList.add(module);
	            } catch (InstantiationException e) {
	                e.printStackTrace();
	            } catch (IllegalAccessException e) {
	                e.printStackTrace();
	            } catch (ClassCastException e) {
	                //e.printStackTrace();
	            }
	        }
        }
        
        return moduleList.toArray(new Module[moduleList.size()]);
    }

    public abstract Module[] getModules();

}

Module
This is what the Module class looked like. It would be extended by the indivdual modules.

package [REDACTED].revisionlib.module;

/**
 * @author [REDACTED]
 * @version 1
 * @since 8/23/2013 6:19 PM
 */
public abstract class Module implements Toggleable, Titled {

    /**
     * Enabled state of the module
     */
    private boolean toggle;
    /**
     * Title of the module
     */
    private String title;

    /**
     * Creates a module with the specified title
     * @param title Module title
     */
    public Module(String title) {
        this.setTitle(title);
    }

    /**
     * Creates a module using the class name as the title
     */
    public Module() {
        this.setTitle(this.getClass().getSimpleName());
    }

    @Override
    /**
     * Sets the toggle state of a module
     * @param toggle target toggle state
     */
    public boolean getToggled() {
        return this.toggle;
    }

   @Override
   /**
    * @return The current toggle state of a module
    */
    public void setToggled(boolean toggle) {
       this.toggle = toggle;
    }

    @Override
    /**
     * Sets the title of the module
     * @param title Target title
     */
    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    /**
     * @returns The title of the module
     */
    public String getTitle() {
        return this.title;
    }

}