0

Initial commit

This commit is contained in:
Pepich 2017-02-01 11:48:49 +01:00
commit bf06c10772
15 changed files with 754 additions and 0 deletions

4
plugin.yml Normal file
View File

@ -0,0 +1,4 @@
name: JavaUtils
version: 1.0.0
authors: [pepich1851]
main: com.redstoner.misc.Main

View File

@ -0,0 +1,14 @@
package com.redstoner.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** The auto register annotation, to be put onto Classes that implement listener when you are too lazy to register the events yourself.
*
* @author Pepich */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRegisterEvents
{}

View File

@ -0,0 +1,14 @@
package com.redstoner.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Debugable annotation, to be added to methods that invoke the Debugger.notifyMethod method for debugging purposes.
*
* @author Pepich */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Debugable
{}

View File

@ -0,0 +1,32 @@
package com.redstoner.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** The Version annotation, to be applied to all Classes that are part of the project.
*
* @author Pepich */
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Version
{
/** The major indicator of the version. Will be used for compatibility detection.
*
* @return the major version as an int */
int major();
int minor();
int revision();
/** The compatibility part of the version number. Will be used for compatibility detection.</br>
* Set to -1 if it is supposed to be always compatible.</br>
* Defaults to 1.
*
* @return the smallest compatible version as an int. */
int compatible() default 1;
}

View File

@ -0,0 +1,75 @@
package com.redstoner.coremods.ModuleLoader;
import java.util.HashMap;
import com.redstoner.annotations.Version;
import com.redstoner.misc.Utils;
import com.redstoner.modules.CoreModule;
import com.redstoner.modules.Module;
/** The module loader, mother of all modules. Responsible for loading and taking care of all modules.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public final class ModuleLoader implements CoreModule
{
private static ModuleLoader instance;
private static final HashMap<Module, String> modules = new HashMap<Module, String>();
static
{
instance = new ModuleLoader();
}
private ModuleLoader()
{}
public static final void addModule(Class<? extends Module> clazz, String name)
{
try
{
modules.put(clazz.newInstance(), name);
}
catch (InstantiationException | IllegalAccessException e)
{
Utils.error("Could not add " + name + " to the list, constructor not accessible.");
}
}
public static final void enableModules()
{
for (Module m : modules.keySet())
{
m.onEnable();
if (m.enabled())
Utils.log("Loaded module " + modules.get(m));
}
}
public static final boolean enableModule(Class<? extends Module> clazz, String name)
{
try
{
Module m = clazz.newInstance();
modules.put(m, name);
m.onEnable();
if (m.enabled())
Utils.log("Loaded module " + modules.get(m));
return m.enabled();
}
catch (InstantiationException | IllegalAccessException e)
{
Utils.error("Could not add " + name + " to the list, constructor not accessible.");
return false;
}
}
@Override
public final String getCommandString()
{
return "";
}
public static void init()
{}
}

View File

@ -0,0 +1,183 @@
package com.redstoner.coremods.debugger;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import com.nemez.cmdmgr.Command;
import com.nemez.cmdmgr.CommandManager;
import com.redstoner.annotations.Debugable;
import com.redstoner.annotations.Version;
import com.redstoner.misc.Main;
import com.redstoner.misc.Utils;
import com.redstoner.modules.CoreModule;
/** The Debugger class, first Module to be loaded, responsible for debug interactions such as subscribing to method calls and getting field values on runtime.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public final class Debugger implements CoreModule, Listener
{
private static Debugger instance;
private static HashMap<CommandSender, ArrayList<String>> subs;
static
{
instance = new Debugger();
}
private Debugger()
{
subs = new HashMap<>();
CommandManager.registerCommand(getCommandString(), instance, Main.plugin);
}
public static void notifyMethod(Object... params)
{
Exception e = new Exception();
String method = e.getStackTrace()[1].getMethodName();
String classname = e.getStackTrace()[1].getClassName();
for (Player p : Bukkit.getOnlinePlayers())
if (subs.containsKey(p))
{
boolean subscribed = false;
for (String s : subs.get(p))
{
if (s.equals(classname + "." + method))
{
subscribed = true;
break;
}
}
if (subscribed)
{
StringBuilder sb = new StringBuilder(method);
sb.append("(");
if (params != null)
{
for (Object obj : params)
{
if (obj == null)
sb.append("NULL");
else
sb.append(obj.toString());
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length());
}
sb.append(")\nTypes:\n");
int i = 0;
for (Object obj : params)
sb.append(i++ + ": " + (obj == null ? "NULL" : obj.getClass().getName()) + "\n");
p.sendMessage(sb.toString());
}
}
CommandSender p = Bukkit.getConsoleSender();
if (subs.containsKey(p))
{
boolean subscribed = false;
for (String s : subs.get(p))
{
if (s.equals(classname + "." + method))
{
subscribed = true;
break;
}
}
if (subscribed)
{
StringBuilder sb = new StringBuilder(method);
sb.append("(");
if (params != null)
{
for (Object obj : params)
{
if (obj == null)
sb.append("NULL");
else
sb.append(obj.toString());
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length());
}
sb.append(")\nTypes:\n");
int i = 0;
for (Object obj : params)
sb.append(i++ + ": " + (obj == null ? "NULL" : obj.getClass().getName()) + "\n");
p.sendMessage(sb.toString());
}
}
else
{
System.err.println("EHH WAT");
}
}
// @noformat
@Override
public String getCommandString()
{
return "command debugger {\n" +
" subscribe [string:classname] [string:methodname] {\n" +
" help Subscribes to all calls of the corresponding debugger method.;\n" +
" perm jutils.debugger.subscribe;\n" +
" run subscribe classname methodname;\n" +
" }\n" +
"}";
}
// @format
@Command(hook = "subscribe")
@Debugable
public boolean subscribeCommand(CommandSender sender, String classname, String methodname)
{
Class<?> clazz = null;
try
{
clazz = Class.forName(classname);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
// TODO: Add error message
return true;
}
boolean found = false;
for (Method m : clazz.getMethods())
{
if (m.getName().matches(methodname))
{
if (m.isAnnotationPresent(Debugable.class))
{
found = true;
if (!subs.containsKey(sender))
subs.put(sender, new ArrayList<String>());
subs.get(sender).add(classname + "." + methodname);
break;
}
}
}
if (!found)
{
System.err.println("2");
// TODO: Add error message
return true;
}
Utils.sendMessage(sender, null, "YAY");
return true;
}
@Debugable
public static void sendMessage(CommandSender recipient, String message)
{
notifyMethod(recipient, message);
}
public static void init()
{}
}

View File

@ -0,0 +1,22 @@
package com.redstoner.exceptions;
import com.redstoner.annotations.Version;
/** To be thrown when a module is not annotated with its version. If this gets thrown, then oh boy, you're in trouble now.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public class MissingVersionException extends Exception
{
private static final long serialVersionUID = 4940161335512222539L;
public MissingVersionException()
{
super();
}
public MissingVersionException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,14 @@
package com.redstoner.misc;
import org.bukkit.command.CommandSender;
import com.redstoner.annotations.Version;
/** Classes implementing this interface can be used to define a filter for the Utils.broadcast method for sending a message to more than one, but less than all users.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = 1)
public interface BroadcastFilter
{
public boolean sendTo(CommandSender recipient);
}

View File

@ -0,0 +1,35 @@
package com.redstoner.misc;
import org.bukkit.plugin.java.JavaPlugin;
import com.redstoner.annotations.Version;
import com.redstoner.coremods.ModuleLoader.ModuleLoader;
import com.redstoner.coremods.debugger.Debugger;
import com.redstoner.modules.adminchat.Adminchat;
import com.redstoner.modules.chatgroups.Chatgroups;
/** Main class. Duh.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public class Main extends JavaPlugin
{
public static JavaPlugin plugin;
@Override
public void onEnable()
{
plugin = this;
Debugger.init();
ModuleLoader.init();
// TODO: Add modules (this also loads them if necessary)
ModuleLoader.addModule(Adminchat.class, "Adminchat");
ModuleLoader.addModule(Chatgroups.class, "Chatrgoups");
// And enable them
ModuleLoader.enableModules();
}
@Override
public void onDisable()
{}
}

View File

@ -0,0 +1,158 @@
package com.redstoner.misc;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.redstoner.annotations.Debugable;
import com.redstoner.annotations.Version;
import com.redstoner.coremods.debugger.Debugger;
import net.md_5.bungee.api.ChatColor;
/** The utils class containing utility functions. Those include but are not limited to sending formatted messages, broadcasts and more.
*
* @author Pepich */
@Version(major = 1, minor = 1, revision = 0, compatible = 1)
public final class Utils
{
/** Hidden constructor. Do not instantiate UTILS classes! :) */
private Utils()
{}
/** This will send a message to the specified recipient. It will generate the module prefix if you want it to.
*
* @param recipient Whom to sent the message to.
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&2MODULE&8]
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise. */
@Debugable
public static void sendMessage(CommandSender recipient, String prefix, String message)
{
Debugger.notifyMethod(recipient, message, prefix);
if (prefix == null)
{
String classname = getCaller();
prefix = "§8[§2" + classname + "§8]: ";
}
recipient.sendMessage(prefix + "§7" + message);
}
/** This will send a message to the specified recipient. It will generate the module prefix if you want it to. Also, this will be logged to console as a warning.
*
* @param recipient Whom to sent the message to.
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise. */
@Debugable
public static void sendErrorMessage(CommandSender recipient, String prefix, String message)
{
Debugger.notifyMethod(recipient, prefix, message);
if (prefix == null)
{
String classname = getCaller();
prefix = "§8[§c" + classname + "§8]: ";
}
recipient.sendMessage(prefix + "§7" + message);
if (!recipient.equals(Bukkit.getConsoleSender()))
Bukkit.getConsoleSender().sendMessage("§c[WARN]: " + prefix + "§7" + message);
}
/** Invokes sendMessage. This method will additionally translate alternate color codes for you.
*
* @param recipient Whom to sent the message to.
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise.
* @param alternateColorCode The alternate color code indicator to use. If set to '&' then "&7" would be translated to "§7". Works with any char. */
public static void sendMessage(CommandSender recipient, String prefix, String message, char alternateColorCode)
{
sendMessage(recipient, ChatColor.translateAlternateColorCodes(alternateColorCode, prefix),
ChatColor.translateAlternateColorCodes(alternateColorCode, message));
}
/** Invokes sendErrorMessage. This method will additionally translate alternate color codes for you.
*
* @param recipient Whom to sent the message to.
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise.
* @param alternateColorCode The alternate color code indicator to use. If set to '&' then "&7" would be translated to "§7". Works with any char. */
public static void sendErrorMessage(CommandSender recipient, String prefix, String message, char alternateColorCode)
{
sendErrorMessage(recipient, ChatColor.translateAlternateColorCodes(alternateColorCode, prefix),
ChatColor.translateAlternateColorCodes(alternateColorCode, message));
}
/** @param message
* @param filter
* @return */
public static int broadcast(String prefix, String message, BroadcastFilter filter)
{
return broadcast(prefix, message, filter, null);
}
/** @param message
* @param filter
* @param log
* @return */
@Debugable
public static int broadcast(String prefix, String message, BroadcastFilter filter, String logmessage)
{
Debugger.notifyMethod(message, filter, logmessage);
if (logmessage != null)
sendMessage(Bukkit.getConsoleSender(), prefix,
logmessage + (filter == null ? " §7(global)" : " §7(filtered)"));
if (filter == null)
{
for (Player p : Bukkit.getOnlinePlayers())
p.sendMessage(message);
return Bukkit.getOnlinePlayers().size();
}
else
{
int count = 0;
for (Player p : Bukkit.getOnlinePlayers())
if (filter.sendTo(p))
{
p.sendMessage(message);
count++;
}
return count;
}
}
/** Used to make an info output to console. Supports &x color codes.
*
* @param message The message to be put into console. Prefixes are automatically generated. */
@Debugable
public static void log(String message)
{
Debugger.notifyMethod(message);
String classname = getCaller();
String prefix = "§8[§2" + classname + "§8]: ";
Bukkit.getConsoleSender().sendMessage(ChatColor.translateAlternateColorCodes('&', prefix + "§7" + message));
}
/** Used to make an error output to console. Supports &x color codes.
*
* @param message The message to be put into console. Prefixes are automatically generated. Color defaults to red if not specified otherwise. */
@Debugable
public static void error(String message)
{
Debugger.notifyMethod(message);
String classname = getCaller();
String prefix = "§c[ERROR]: §8[§c" + classname + "§8]: ";
Bukkit.getConsoleSender().sendMessage(ChatColor.translateAlternateColorCodes('&', prefix + "§7" + message));
}
/** This method will find the next parent caller and return their class name, omitting package names.
*
* @return */
private static final String getCaller()
{
StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
String classname = "Utils";
for (int i = 0; classname.equals("Utils"); i++)
{
classname = stackTrace[i].getClassName().replaceAll(".*\\.", "");
}
return classname;
}
}

View File

@ -0,0 +1,46 @@
package com.redstoner.misc;
import com.redstoner.annotations.Version;
import com.redstoner.exceptions.MissingVersionException;
/** This class can be used to compare modules against the loader version or against each other to prevent dependency issues.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public final class VersionHelper
{
private VersionHelper()
{}
/** Checks two modules versions for compatibility.
*
* @param base The base to compare to.
* @param module The module to compare.
* @return true, when the Major version of the base is bigger than the compatible version of the module, and the Version number of the base is smaller or equal to the Version number of the module.
* @throws MissingVersionException When one of the parameters is not annotated with a @Version annotation. */
public static boolean isCompatible(Class<?> base, Class<?> module) throws MissingVersionException
{
if (!base.isAnnotationPresent(Version.class))
throw new MissingVersionException("The base object is not annotated with a version.");
if (!module.isAnnotationPresent(Version.class))
throw new MissingVersionException("The module is not annotated with a version.");
Version baseVersion = base.getClass().getAnnotation(Version.class);
Version moduleVersion = module.getClass().getAnnotation(Version.class);
if (baseVersion.major() > moduleVersion.major())
return false;
return baseVersion.major() >= moduleVersion.compatible();
}
/** Returns the version of a given class as a String.
*
* @param clazz The class to grab the version number from.
* @return The version number of the class in format major.minor.revision.compatible.
* @throws MissingVersionException If the class is not annotated with @Version. */
public static String getVersion(Class<?> clazz) throws MissingVersionException
{
if (!clazz.isAnnotationPresent(Version.class))
throw new MissingVersionException("The given class is not associated with a version.");
Version ver = clazz.getAnnotation(Version.class);
return ver.major() + "." + ver.minor() + "." + ver.revision() + "." + ver.compatible();
}
}

View File

@ -0,0 +1,18 @@
package com.redstoner.modules;
import com.redstoner.annotations.Version;
/** This class shall be used for "CoreModules", which are acting on a lower level than modules and are also exempted from being disabled or reloaded on the go.</br>
* Examples are the ModuleLoader and the Debugger.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = -1)
public interface CoreModule extends Module
{
/** Core modules should always be enabled. */
@Override
public default boolean enabled()
{
return true;
}
}

View File

@ -0,0 +1,28 @@
package com.redstoner.modules;
import com.redstoner.annotations.Version;
/** Interface for the Module class. Modules must always have an empty constructor to be invoked by the ModuleLoader.
*
* @author Pepich */
@Version(major = 1, minor = 0, revision = 0, compatible = 1)
public interface Module
{
/** Will be called when the module gets enabled. */
public default void onEnable()
{}
/** Will be called when the module gets disabled. */
public default void onDisable()
{}
/** Will be called to check if a module is enabled or not.
*
* @return The status of the module, true when enabled, false when not. */
public boolean enabled();
/** Gets called on registration of the module.
*
* @return The String used for the CommandManager to register the commands. */
public String getCommandString();
}

View File

@ -0,0 +1,39 @@
package com.redstoner.modules.adminchat;
import com.redstoner.annotations.AutoRegisterEvents;
import com.redstoner.annotations.Version;
import com.redstoner.modules.Module;
/** AdminChat module. Allows staff to chat to other staff using /ac \<message\> as well as a one char prefix or a toggle.
*
* @author Pepich */
@AutoRegisterEvents
@Version(major = 1, minor = 0, revision = 0, compatible = 1)
public class Adminchat implements Module
{
private boolean enabled = false;
@Override
public void onEnable()
{
this.enabled = true;
}
@Override
public void onDisable()
{
this.enabled = false;
}
@Override
public boolean enabled()
{
return enabled;
}
@Override
public String getCommandString()
{
return null;
}
}

View File

@ -0,0 +1,72 @@
package com.redstoner.modules.chatgroups;
import java.util.UUID;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import com.nemez.cmdmgr.Command;
import com.redstoner.annotations.AutoRegisterEvents;
import com.redstoner.annotations.Version;
import com.redstoner.modules.Module;
/** The ChatGroups module. Allows people to have private sub-chats that can be accessed via a single char prefix or a toggle.
*
* @author Pepich */
@AutoRegisterEvents
@Version(major = 1, minor = 0, revision = 0, compatible = 1)
public class Chatgroups implements Module, Listener
{
private boolean enabled = false;
@Override
public void onEnable()
{
enabled = true;
}
@Override
public void onDisable()
{
enabled = false;
}
@Override
public boolean enabled()
{
return enabled;
}
@Override
public String getCommandString()
{
return "";
}
@Command(hook = "cgkey")
public void cgKaeyCommand(String key)
{}
@Command(hook = "cgtoggle")
public void cgToggleCommand()
{}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event)
{
if (event.getMessage().startsWith(":"))
{
event.setCancelled(true);
sendToGroup(getGroup(event.getPlayer().getUniqueId()), event.getMessage().replaceFirst(":", ""));
}
}
public static String getGroup(UUID player)
{
return "";
}
private void sendToGroup(String group, String message)
{}
}