commit c230a353b129586b70dee49d02c9b851f54d6eb9 Author: Clisprail Date: Wed Apr 3 20:21:15 2013 +0200 First commit diff --git a/parabotv2/.gitignore b/parabotv2/.gitignore new file mode 100644 index 0000000..6a51d19 --- /dev/null +++ b/parabotv2/.gitignore @@ -0,0 +1,5 @@ +bin +.classpath +.project +.settings +.metadata \ No newline at end of file diff --git a/parabotv2/asm-debug-all-4.0.jar b/parabotv2/asm-debug-all-4.0.jar new file mode 100644 index 0000000..9562c99 Binary files /dev/null and b/parabotv2/asm-debug-all-4.0.jar differ diff --git a/parabotv2/src/org/parabot/Landing.java b/parabotv2/src/org/parabot/Landing.java new file mode 100644 index 0000000..3f9530e --- /dev/null +++ b/parabotv2/src/org/parabot/Landing.java @@ -0,0 +1,24 @@ +package org.parabot; + +import javax.swing.UIManager; + +import org.parabot.core.ui.ServerSelector; + +/** + * Parabot X - A revolution in bot clients + * + * @author Clisprail + * @author Matt, Dane, Queue, Parameter + * @version 2.0 + */ +public class Landing { + + public static void main(String... args) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Throwable t) { + t.printStackTrace(); + } + ServerSelector.getInstance().setVisible(true); + } +} diff --git a/parabotv2/src/org/parabot/core/Context.java b/parabotv2/src/org/parabot/core/Context.java new file mode 100644 index 0000000..e8e14ce --- /dev/null +++ b/parabotv2/src/org/parabot/core/Context.java @@ -0,0 +1,121 @@ +package org.parabot.core; + +import java.applet.Applet; +import java.util.HashMap; +import java.util.TimerTask; + +import org.parabot.core.asm.ASMClassLoader; +import org.parabot.core.bot.loader.BotLoader; +import org.parabot.core.classpath.ClassPath; +import org.parabot.core.ui.components.GamePanel; +import org.parabot.environment.servers.ServerProvider; + +/** + * Game context + * @author Clisprail + * + */ +public class Context { + private static HashMap threadGroups = new HashMap(); + private static int id = 1; + + private ASMClassLoader classLoader = null; + private ClassPath classPath = null; + private ServerProvider serverProvider = null; + private int tab = 0; + private Applet gameApplet = null; + + public boolean added = false; + + public Context(final ServerProvider serverProvider) { + threadGroups.put(Thread.currentThread().getThreadGroup(), this); + tab = id; + this.serverProvider = serverProvider; + id++; + this.classPath = new ClassPath(); + } + + /** + * Sets the ServerProvider class loader + * @param serverEnvironment + */ + public void setEnvironment(ASMClassLoader serverEnvironment) { + classLoader = new BotLoader(classPath, serverEnvironment); + } + + /** + * ClassPath + * @return classpath + */ + public ClassPath getClassPath() { + return classPath; + } + + /** + * Determines if applet has been set + * @return true if set + */ + public boolean appletSet() { + return gameApplet != null; + } + + /** + * Gets game applet + * @return applet + */ + public Applet getApplet() { + return gameApplet; + } + + /** + * Resolves the context from threadgroup + * @return context + */ + public static Context resolve() { + return threadGroups.get(Thread.currentThread().getThreadGroup()); + } + + /** + * Loads the game + */ + public void load() { + serverProvider.parseJar(); + gameApplet = serverProvider.fetchApplet(); + final GamePanel panel = GamePanel.getInstance(); + panel.removeLoader(); + panel.setContext(this); + gameApplet.setSize(765, 503); + java.util.Timer t = new java.util.Timer(); + t.schedule(new TimerTask() { + @Override + public void run() { + gameApplet.setBounds(0, 0, 765, 503); + } + }, 1000); + } + + /** + * Gets the server prodiver belonging to this context + * @return server provider + */ + public ServerProvider getServerProvider() { + return serverProvider; + } + + /** + * Gets class loader from this context + * @return class loader + */ + public ASMClassLoader getASMClassLoader() { + return classLoader; + } + + public static int getID() { + return id; + } + + public int getTab() { + return tab; + } + +} diff --git a/parabotv2/src/org/parabot/core/Core.java b/parabotv2/src/org/parabot/core/Core.java new file mode 100644 index 0000000..780b6b4 --- /dev/null +++ b/parabotv2/src/org/parabot/core/Core.java @@ -0,0 +1,27 @@ +package org.parabot.core; + +/** + * + * @author Clisprail + * + */ +public class Core { + private static boolean devMode = false; + + /** + * Enables the developers mode + */ + public static void enableDevMode() { + devMode = true; + } + + /** + * Determines if bot is in developers mode + * @return true if bot is in developers mode + */ + public static boolean isDevMode() { + return devMode; + } + + +} diff --git a/parabotv2/src/org/parabot/core/asm/ASMClassLoader.java b/parabotv2/src/org/parabot/core/asm/ASMClassLoader.java new file mode 100644 index 0000000..107a649 --- /dev/null +++ b/parabotv2/src/org/parabot/core/asm/ASMClassLoader.java @@ -0,0 +1,92 @@ +package org.parabot.core.asm; + + +import java.net.URL; +import java.security.AllPermission; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; +import org.parabot.core.classpath.ClassPath; + +/** + * + * @author Clisprail + * @author Matt + * + */ +public class ASMClassLoader extends ClassLoader { + + public Map>classCache = new HashMap>(); + public ClassPath classPath = null; + + public ASMClassLoader(final ClassPath classPath) { + this.classPath = classPath; + } + + @Override + protected URL findResource(String name) { + if (getSystemResource(name) == null) { + if (classPath.resources.containsKey(name)) + return classPath.resources.get(name); + else + return null; + } else + return getSystemResource(name); + } + + public void addClassToCache(final Class clazz) { + String clazzName = clazz.getName().replace('.', '/'); + classCache.put(clazzName, clazz); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return findClass(name); + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + String key = name.replace('.', '/'); + if(classCache.containsKey(key)) { + return classCache.get(key); + } + + ClassNode node = classPath.classes.get(key); + if (node != null) { + classPath.classes.remove(key); + Classc = nodeToClass(node); + classCache.put(key, c); + return c; + } else + return super.getSystemClassLoader().loadClass(name); + } + + public final Class nodeToClass(ClassNode node) { + if (super.findLoadedClass(node.name) != null) + return findLoadedClass(node.name); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + node.accept(cw); + byte[] b = cw.toByteArray(); + return defineClass(node.name.replace('/', '.'), b, 0, b.length, + getDomain()); + } + + private final ProtectionDomain getDomain() { + CodeSource code = new CodeSource(null, (Certificate[]) null); + return new ProtectionDomain(code, getPermissions()); + } + + private final Permissions getPermissions() { + Permissions permissions = new Permissions(); + permissions.add(new AllPermission()); + return permissions; + } + +} + diff --git a/parabotv2/src/org/parabot/core/asm/Injector.java b/parabotv2/src/org/parabot/core/asm/Injector.java new file mode 100644 index 0000000..e8b38cc --- /dev/null +++ b/parabotv2/src/org/parabot/core/asm/Injector.java @@ -0,0 +1,5 @@ +package org.parabot.core.asm; + +public class Injector { + +} diff --git a/parabotv2/src/org/parabot/core/asm/adapters/AddGetterAdapter.java b/parabotv2/src/org/parabot/core/asm/adapters/AddGetterAdapter.java new file mode 100644 index 0000000..06903a2 --- /dev/null +++ b/parabotv2/src/org/parabot/core/asm/adapters/AddGetterAdapter.java @@ -0,0 +1,154 @@ +package org.parabot.core.asm.adapters; + +import java.lang.reflect.Modifier; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +/** + * Adds a method into a Classnode which returns a field + * + * @author Clisprail + * + */ +public class AddGetterAdapter implements Opcodes { + private ClassNode into = null; + private ClassNode fieldLocation = null; + private FieldNode fieldNode = null; + private String methodName = null; + private String returnDesc = null; + private boolean staticField = false; + private boolean staticMethod = false; + + /** + * + * @param into - classnode to inject getter method in + * @param fieldLocation - classnode where field is located + * @param fieldName - field name to get + * @param methodName - method name of getter + * @param returnDesc - return type of method, can be null for default return + * @param staticMethod - pass true if you want the method to be static + */ + public AddGetterAdapter(final ClassNode into, final ClassNode fieldLocation, final FieldNode fieldNode, + final String methodName, final String returnDesc, final boolean staticMethod) { + this.into = into; + this.fieldLocation = fieldLocation; + this.fieldNode = fieldNode; + this.methodName = methodName; + this.returnDesc = returnDesc == null ? fieldNode.desc : returnDesc; + this.staticField = Modifier.isStatic(fieldNode.access); + this.staticMethod = staticMethod; + } + + /** + * + * @param fieldLocation + * @param fieldNode + * @param methodName + */ + public AddGetterAdapter(final ClassNode fieldLocation, final FieldNode fieldNode, final String methodName) { + this.into = fieldLocation; + this.fieldLocation = fieldLocation; + this.fieldNode = fieldNode; + this.methodName = methodName; + this.returnDesc = fieldNode.desc; + this.staticField = Modifier.isStatic(fieldNode.access); + this.staticMethod = false; + } + + + /** + * Validates if this getter can be injected, if not a runtime exception is thrown + */ + public void validate() { + if(methodName == null) { + throw new RuntimeException("Null method name"); + } + if(into == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("Into ClassNode is null, at : ").append(methodName).append("()"); + throw new RuntimeException(sb.toString()); + } + if(fieldNode == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("FieldLocation ClassNode is null, at : ").append(methodName).append("()"); + throw new RuntimeException(sb.toString()); + } + if(fieldNode == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("FieldNode is null, at : ").append(methodName).append("()"); + throw new RuntimeException(sb.toString()); + } + for(final MethodNode methodNode : into.methods) { + if(methodNode.name.equals(methodName)) { + final Type[] args = Type.getArgumentTypes(methodNode.desc); + if(args != null && args.length != 0) { + continue; + } + final StringBuilder sb = new StringBuilder(); + sb.append("Duplicated method detected. ").append(methodName).append("() in ").append(into.name); + throw new RuntimeException(sb.toString()); + } + } + } + + /** + * Injects this the method getter + */ + public void inject() { + MethodNode method = new MethodNode(ACC_PUBLIC | (staticMethod ? ACC_STATIC : 0), methodName, "()" + returnDesc, null, null); + if (staticField) { + method.visitVarInsn(ALOAD, 0); + } + method.visitFieldInsn(staticField ? GETSTATIC : GETFIELD, fieldLocation.name, fieldNode.name, fieldNode.desc); + if (!fieldNode.desc.equals(returnDesc)) { + if (returnDesc.contains("L")) { + if (!returnDesc.contains("[")) { + method.visitTypeInsn(CHECKCAST,returnDesc.replaceFirst("L", "").replaceAll(";", "")); + } else { + method.visitTypeInsn(CHECKCAST, returnDesc); + } + } + } + + if (fieldNode.desc.equals("J") && returnDesc.equals("I")) + method.visitInsn(L2I); + + method.visitInsn(getReturnOpcode(returnDesc)); + method.visitMaxs(0, 0); + into.methods.add(method); + } + + /** + * Return right opcode for desc + * @param desc + * @return return opcode + */ + private static int getReturnOpcode(String desc) { + desc = desc.substring(desc.indexOf("L") + 1); + if (desc.length() > 1) { + return ARETURN; + } + final char c = desc.charAt(0); + switch (c) + { + case 'I': + case 'Z': + case 'B': + case 'S': + case 'C': + return IRETURN; + case 'J': + return LRETURN; + case 'F': + return FRETURN; + case 'D': + return DRETURN; + } + throw new RuntimeException("Wrong desc type: " + c); + } + +} \ No newline at end of file diff --git a/parabotv2/src/org/parabot/core/bot/loader/BotLoader.java b/parabotv2/src/org/parabot/core/bot/loader/BotLoader.java new file mode 100644 index 0000000..104d22d --- /dev/null +++ b/parabotv2/src/org/parabot/core/bot/loader/BotLoader.java @@ -0,0 +1,42 @@ +package org.parabot.core.bot.loader; + +import org.objectweb.asm.tree.ClassNode; +import org.parabot.core.asm.ASMClassLoader; +import org.parabot.core.classpath.ClassPath; + +/** + * Handles client class calls + * + * @author Clisprail + * + */ +public class BotLoader extends ASMClassLoader { + private ASMClassLoader serverProvider = null; + + public BotLoader(ClassPath classPath, ASMClassLoader serverProvider) { + super(classPath); + this.serverProvider = serverProvider; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return findClass(name); + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + String key = name.replace('.', '/'); + if(serverProvider.classCache.containsKey(key)) { + return serverProvider.classCache.get(key); + } + ClassNode node = serverProvider.classPath.classes.get(key); + if (node != null) { + serverProvider.classPath.classes.remove(key); + Classc = serverProvider.nodeToClass(node); + serverProvider.classCache.put(key, c); + return c; + } + return super.findClass(name); + } + +} diff --git a/parabotv2/src/org/parabot/core/classpath/ClassPath.java b/parabotv2/src/org/parabot/core/classpath/ClassPath.java new file mode 100644 index 0000000..2c9695c --- /dev/null +++ b/parabotv2/src/org/parabot/core/classpath/ClassPath.java @@ -0,0 +1,115 @@ +package org.parabot.core.classpath; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + +/** + * + * @author Clisprail + * @author Matt + */ +public class ClassPath { + public final HashMap classes = new HashMap(); + public final Map resources = new HashMap(); + + /** + * Adds jar to this classpath + * @param jarLocation + */ + public void addJar(final String jarLocation) { + JarParser.parseJar(this, jarLocation); + } + + /** + * Finds and loads all classes/jar files in folder + * + * @param file to find class / jar files + * @param root + */ + public void loadClasses(final File f, File root) { + if (f == null) + return; + if (!f.exists()) { + f.mkdirs(); + } + if (root == null) { + root = f; + } + for (File f1 : f.listFiles()) { + if (f1 == null) { + continue; + } else if (f1.isDirectory()) { + loadClasses(f1, root); + } else { + try (FileInputStream fin = new FileInputStream(f1)) { + if (f1.getName().endsWith(".class")) + loadClass(fin); + else if (f.equals(root) && f1.getName().endsWith(".jar")) { + loadClasses(f1.toURI().toURL()); + } else { + String path = f1.toURI().relativize(root.toURI()) + .getPath(); + Resources.loadResource(this, path, fin); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + /** + * Loads classes from zip/jar files + * + * @param url to file + */ + private void loadClasses(URL u) { + try (ZipInputStream zin = new ZipInputStream(u.openStream())) { + ZipEntry e; + while ((e = zin.getNextEntry()) != null) { + if (e.isDirectory()) + continue; + if (e.getName().endsWith(".class")) { + loadClass(zin); + } else { + Resources.loadResource(this, e.getName(), zin); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Loads class from input stream + * + * @param inputstream + * @throws IOException + */ + private void loadClass(InputStream in) throws IOException { + ClassReader cr = new ClassReader(in); + ClassNode cn = new ClassNode(); + cr.accept(cn, 0); + classes.put(cn.name, cn); + } + + /** + * Dumps the classnodes into a jar + * + * @param jarName + */ + public void dump(final String jarName) { + JarDumper.dump(this, jarName); + } + +} diff --git a/parabotv2/src/org/parabot/core/classpath/JarDumper.java b/parabotv2/src/org/parabot/core/classpath/JarDumper.java new file mode 100644 index 0000000..f9eaa52 --- /dev/null +++ b/parabotv2/src/org/parabot/core/classpath/JarDumper.java @@ -0,0 +1,41 @@ +package org.parabot.core.classpath; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +/** + * + * @author Clisprail + * + */ +public class JarDumper { + + /** + * Dumps classnodes to a jar file + * @param classPath + * @param fileName + */ + public static void dump(final ClassPath classPath, final String fileName) { + try { + FileOutputStream stream = new FileOutputStream(new File(fileName)); + JarOutputStream out = new JarOutputStream(stream); + for (ClassNode cn : classPath.classes.values()) { + JarEntry je = new JarEntry(cn.name + ".class"); + out.putNextEntry(je); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cn.accept(cw); + out.write(cw.toByteArray()); + } + out.close(); + stream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/parabotv2/src/org/parabot/core/classpath/JarParser.java b/parabotv2/src/org/parabot/core/classpath/JarParser.java new file mode 100644 index 0000000..d7dc54c --- /dev/null +++ b/parabotv2/src/org/parabot/core/classpath/JarParser.java @@ -0,0 +1,52 @@ +package org.parabot.core.classpath; + +import java.net.JarURLConnection; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + +/** + * + * A class for parsing a jar file + * + * @author Clisprail + * + */ +public class JarParser { + + /** + * Parses a jar from an URL + * @param classPath + * @param jarLocation + */ + public static void parseJar(final ClassPath classPath, final String jarLocation) { + try { + URL jarURL = new URL("jar:" + jarLocation + "!/"); + JarURLConnection jarConnection = (JarURLConnection) jarURL + .openConnection(); + JarFile theJar = jarConnection.getJarFile(); + Enumeration en = theJar.entries(); + while (en.hasMoreElements()) { + JarEntry entry = (JarEntry) en.nextElement(); + if (entry.getName().endsWith(".class")) { + ClassReader cr = new ClassReader( + theJar.getInputStream(entry)); + ClassNode cn = new ClassNode(); + //cr.accept(cn, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + cr.accept(cn, 0); + classPath.classes.put(cn.name, cn); + } else if (!entry.isDirectory() + && !entry.getName().startsWith("META-INF")) { + Resources.loadResource(classPath, entry.getName(), theJar.getInputStream(entry)); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/parabotv2/src/org/parabot/core/classpath/Resources.java b/parabotv2/src/org/parabot/core/classpath/Resources.java new file mode 100644 index 0000000..3dff191 --- /dev/null +++ b/parabotv2/src/org/parabot/core/classpath/Resources.java @@ -0,0 +1,37 @@ +package org.parabot.core.classpath; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + * @author Clisprail + * @author Matt + */ +public class Resources { + + /** + * Dumps a resource from a input stream + * @param classPath + * @param name + * @param inputstream + * @throws IOException + */ + public static void loadResource(final ClassPath classPath, final String name, final InputStream in) + throws IOException { + File f = File.createTempFile("bot", ".tmp", new File("./")); + f.deleteOnExit(); + try (OutputStream out = new FileOutputStream(f)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) != -1) + out.write(buffer, 0, len); + } catch (IOException e) { + } + classPath.resources.put(name, f.toURI().toURL()); + } + +} diff --git a/parabotv2/src/org/parabot/core/logging/LabelLogHandler.java b/parabotv2/src/org/parabot/core/logging/LabelLogHandler.java new file mode 100644 index 0000000..f0a6491 --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/LabelLogHandler.java @@ -0,0 +1,42 @@ +package org.parabot.core.logging; + +import java.awt.Color; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import javax.swing.JLabel; + +public class LabelLogHandler extends Handler +{ + public final JLabel label = new JLabel(); + private final Color defaultColor; + + public LabelLogHandler() + { + super(); + defaultColor = label.getForeground(); + } + + @Override + public void close() throws SecurityException { + } + + @Override + public void flush() { + } + + @Override + public void publish(final LogRecord record) { + StringBuilder b = new StringBuilder(record.getMessage()); + + if (record.getLevel().intValue() > Level.WARNING.intValue()) { + label.setForeground(new Color(0xcc0000)); + } else { + label.setForeground(defaultColor); + b.append(" ..."); + } + + label.setText(new String(b)); + } +} diff --git a/parabotv2/src/org/parabot/core/logging/LogFormatter.java b/parabotv2/src/org/parabot/core/logging/LogFormatter.java new file mode 100644 index 0000000..b17a42f --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/LogFormatter.java @@ -0,0 +1,49 @@ +package org.parabot.core.logging; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +public class LogFormatter extends Formatter +{ + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private final boolean appendNewLine; + + public LogFormatter() + { + this(true); + } + + public LogFormatter(final boolean appendNewLine) + { + this.appendNewLine = appendNewLine; + } + + @Override + public String format(final LogRecord record) { + final StringBuilder result = new StringBuilder().append("[").append(record.getLevel().getName()).append("] ").append(new Date(record.getMillis())).append(": ").append(record.getLoggerName()).append(": ").append(record.getMessage()).append(throwableToString(record.getThrown())); + if (appendNewLine) { + result.append(LogFormatter.LINE_SEPARATOR); + } + return result.toString(); + } + + @Override + public String formatMessage(final LogRecord record) { + return String.format(record.getMessage()); + } + + private String throwableToString(final Throwable t) { + if (t != null) { + final Writer exception = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(exception); + t.printStackTrace(printWriter); + return exception.toString(); + } + return ""; + } +} diff --git a/parabotv2/src/org/parabot/core/logging/LogOutputStream.java b/parabotv2/src/org/parabot/core/logging/LogOutputStream.java new file mode 100644 index 0000000..d929eff --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/LogOutputStream.java @@ -0,0 +1,50 @@ +package org.parabot.core.logging; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Paris + */ +public class LogOutputStream extends OutputStream { + protected boolean hasBeenClosed = false; + protected Logger category; + protected Level priority; + protected StringBuilder buffer; + + public LogOutputStream(final Logger category, final Level priority) { + this.priority = priority; + this.category = category; + buffer = new StringBuilder(); + } + + @Override + public void close() { + flush(); + hasBeenClosed = true; + } + + @Override + public void flush() { + final String txt = buffer.toString().replace("\\s+$", ""); + if (txt.trim().length() != 0) { + category.log(priority, txt); + } + reset(); + } + + private void reset() { + buffer.setLength(0); + } + + @Override + public void write(final int b) throws IOException { + if (hasBeenClosed) { + throw new IOException("The stream has been closed."); + } else if (b != 0) { + buffer.append((char) (b & 0xff)); + } + } +} \ No newline at end of file diff --git a/parabotv2/src/org/parabot/core/logging/LogTextArea.java b/parabotv2/src/org/parabot/core/logging/LogTextArea.java new file mode 100644 index 0000000..5dfdd1d --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/LogTextArea.java @@ -0,0 +1,242 @@ +package org.parabot.core.logging; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Formatter; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import javax.swing.AbstractListModel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTextPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +/** + * Non swing methods are thread safe. + */ +@SuppressWarnings("rawtypes") +public class LogTextArea extends JList +{ + private static final int MAX_ENTRIES = 100; + private static final Rectangle BOTTOM_OF_WINDOW = new Rectangle(0, Integer.MAX_VALUE, 0, 0); + private static final long serialVersionUID = 0; + private final LogQueue logQueue = new LogQueue(); + private final LogAreaListModel model = new LogAreaListModel(); + private final Runnable scrollToBottom = new Runnable() + { + public void run() { + scrollRectToVisible(LogTextArea.BOTTOM_OF_WINDOW); + } + }; + + private static final Formatter formatter = new Formatter() + { + private final SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss"); + + @Override + public String format(final LogRecord record) { + final String[] className = record.getLoggerName().split("\\."); + final String name = className[className.length - 1]; + final int maxLen = 16; + final String append = "..."; + + return String.format("[%s] %-" + maxLen + "s %s %s", dateFormat.format(record.getMillis()), name.length() > maxLen ? name.substring(0, maxLen - append.length()) + append : name, record.getMessage(), throwableToString(record.getThrown())); + } + }; + + private static String throwableToString(final Throwable t) { + if (t != null) { + final Writer exception = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(exception); + t.printStackTrace(printWriter); + return exception.toString(); + } + return ""; + } + + private static final Formatter copyPasteFormatter = new LogFormatter(false); + + @SuppressWarnings("unchecked") + public LogTextArea() + { + setModel(model); + setCellRenderer(new Renderer()); + setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + String fontName = Font.MONOSPACED; + for (final Font font : GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts()) { + final String name = font.getName(); + if (name.matches("Monaco|Consolas")) { + fontName = name; + break; + } + } + setFont(new Font(fontName, Font.PLAIN, 12)); + + new Thread(logQueue, "LogGuiQueue").start(); + } + + /** + * Logs a new entry to be shown in the list. Thread safe. + * + * @param logRecord + * The entry. + */ + public void log(final LogRecord logRecord) { + logQueue.queue(new WrappedLogRecord(logRecord)); + } + + private class LogAreaListModel extends AbstractListModel + { + private static final long serialVersionUID = 0; + + private List records = new ArrayList(LogTextArea.MAX_ENTRIES); + + public void addAllElements(final List obj) { + records.addAll(obj); + if (getSize() > LogTextArea.MAX_ENTRIES) { + records.subList(0, (getSize() - LogTextArea.MAX_ENTRIES)).clear(); + + fireContentsChanged(this, 0, (getSize() - 1)); + } else { + fireIntervalAdded(this, (getSize() - 1), (getSize() - 1)); + } + } + + public Object getElementAt(final int index) { + return records.get(index); + } + + public int getSize() { + return records.size(); + } + } + + /** + * Flushes every #FLUSH_RATE (milliseconds) + */ + private class LogQueue implements Runnable + { + public static final int FLUSH_RATE = 1000; + private final Object lock = new Object(); + private List queue = new ArrayList(100); + + public void queue(final WrappedLogRecord record) { + synchronized (lock) { + queue.add(record); + } + } + + public void run() { + while (true) { + List toFlush = null; + + synchronized (lock) { + if (queue.size() != 0) { + toFlush = new ArrayList(queue); + queue = queue.subList(0, 0); + } + } + if (toFlush != null) { // Hold the lock for as little time as + // possible + model.addAllElements(toFlush); + SwingUtilities.invokeLater(scrollToBottom); + } + try { + Thread.sleep(LogQueue.FLUSH_RATE); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + } + + private static class Renderer implements ListCellRenderer + { + private final Border EMPTY_BORDER = new EmptyBorder(1, 1, 1, 1); + private final Border SELECTED_BORDER = UIManager.getBorder("List.focusCellHighlightBorder"); + private final Color DARK_GREEN = new Color(0, 0xcc, 0), DARK_RED = new Color(0xcc, 0, 0), DARK_PINK = new Color(0xff, 0, 0x66); + + public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) { + if (!(value instanceof WrappedLogRecord)) { + return new JLabel(); + } + final WrappedLogRecord wlr = (WrappedLogRecord) value; + + final JTextPane result = new JTextPane(); + result.setDragEnabled(true); + result.setText(wlr.formatted); + result.setComponentOrientation(list.getComponentOrientation()); + result.setFont(list.getFont()); + result.setBorder(cellHasFocus || isSelected ? SELECTED_BORDER : EMPTY_BORDER); + + result.setForeground(Color.BLACK); + + if (wlr.record.getLevel() == Level.SEVERE) { + result.setBackground(DARK_RED); + result.setForeground(Color.BLACK); + } + + if (wlr.record.getLevel() == Level.WARNING) { + result.setForeground(DARK_PINK); + } + + if (wlr.record.getLevel() == Level.FINE || wlr.record.getLevel() == Level.FINER || wlr.record.getLevel() == Level.FINEST) { + result.setForeground(DARK_GREEN); + } + + final Object[] parameters = wlr.record.getParameters(); + if (parameters != null) { + for (final Object parameter : parameters) { + if (parameter == null) { + continue; + } + + if (parameter instanceof Color) { + result.setForeground((Color) parameter); + } else if (parameter instanceof Font) { + result.setFont((Font) parameter); + } + } + } + + return result; + } + + } + + /** + * Wrap the log records so we can control the copy paste text (via #toString) + */ + private class WrappedLogRecord + { + public final LogRecord record; + public final String formatted; + + public WrappedLogRecord(final LogRecord record) + { + this.record = record; + formatted = LogTextArea.formatter.format(record); + } + + @Override + public String toString() { + return LogTextArea.copyPasteFormatter.format(record); + } + } +} diff --git a/parabotv2/src/org/parabot/core/logging/SystemConsoleHandler.java b/parabotv2/src/org/parabot/core/logging/SystemConsoleHandler.java new file mode 100644 index 0000000..52eeb14 --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/SystemConsoleHandler.java @@ -0,0 +1,13 @@ +package org.parabot.core.logging; + +import java.util.logging.ConsoleHandler; + +/** + * Logs to System.out + */ +public class SystemConsoleHandler extends ConsoleHandler { + public SystemConsoleHandler() { + super(); + setOutputStream(System.out); + } +} diff --git a/parabotv2/src/org/parabot/core/logging/TextAreaLogHandler.java b/parabotv2/src/org/parabot/core/logging/TextAreaLogHandler.java new file mode 100644 index 0000000..cfe11f6 --- /dev/null +++ b/parabotv2/src/org/parabot/core/logging/TextAreaLogHandler.java @@ -0,0 +1,22 @@ +package org.parabot.core.logging; + +import java.util.logging.Handler; +import java.util.logging.LogRecord; + + +public class TextAreaLogHandler extends Handler { + public static final LogTextArea TEXT_AREA = new LogTextArea(); + + @Override + public void close() throws SecurityException { + } + + @Override + public void flush() { + } + + @Override + public void publish(final LogRecord record) { + TextAreaLogHandler.TEXT_AREA.log(record); + } +} diff --git a/parabotv2/src/org/parabot/core/parsers/ServerManifestParser.java b/parabotv2/src/org/parabot/core/parsers/ServerManifestParser.java new file mode 100644 index 0000000..2645397 --- /dev/null +++ b/parabotv2/src/org/parabot/core/parsers/ServerManifestParser.java @@ -0,0 +1,12 @@ +package org.parabot.core.parsers; + +/** + * + * @author Clisprail + * + */ +public class ServerManifestParser { + + + +} diff --git a/parabotv2/src/org/parabot/core/ui/BotUI.java b/parabotv2/src/org/parabot/core/ui/BotUI.java new file mode 100644 index 0000000..545ba3b --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/BotUI.java @@ -0,0 +1,98 @@ +package org.parabot.core.ui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.GroupLayout; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.WindowConstants; + +import org.parabot.core.Context; +import org.parabot.core.ui.components.BotToolbar; +import org.parabot.core.ui.components.GamePanel; +import org.parabot.core.ui.components.LogArea; +import org.parabot.core.ui.images.Images; +import org.parabot.core.ui.utils.Center; + +/** + * Bot frame + * + * @author Clisprail + * + */ +public class BotUI extends JFrame { + + private static final long serialVersionUID = 1L; + private static BotUI instance = null; + + public static BotUI getInstance() { + return instance == null ? instance = new BotUI() : instance; + } + + public BotUI() { + JMenuBar bar = new JMenuBar(); + JMenu file = new JMenu("File"); + JMenuItem screenshot = new JMenuItem("Screenshot"); + screenshot.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + new Thread(new Runnable() { + @Override + public void run() { + final Context c = GamePanel.getInstance().context; + GamePanel.getInstance().setContext(null); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + GamePanel.getInstance().setContext(c); + } + }).start(); + } + }); + file.add(screenshot); + bar.add(file); + + JScrollPane textPane = LogArea.getInstance(); + GroupLayout layout = new GroupLayout(getContentPane()); + JToolBar tool = BotToolbar.getInstance(); + GamePanel pane = GamePanel.getInstance(); + pane.addLoader(); + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + getContentPane().setLayout(layout); + setJMenuBar(bar); + setDefaultLookAndFeelDecorated(true); + setTitle("parabot v2"); + setIconImage(Images.getResource("/org/parabot/core/ui/images/icon.png")); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setResizable(false); + Center.centerFramea(this, 775, 683); + layout.setHorizontalGroup(layout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout + .createSequentialGroup() + .addContainerGap() + .addGroup(layout + .createParallelGroup(GroupLayout.Alignment.LEADING))) + .addComponent(tool, 768, 768, 768) + .addComponent(pane, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(textPane, 768, 768, 768)); + layout.setVerticalGroup(layout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout + .createSequentialGroup() + .addComponent(tool, 30, 30, 30) + .addComponent(pane, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(textPane, 100, 100, 100) + .addContainerGap(58, Short.MAX_VALUE))); + LogArea.log("Welcome to Parabot v2"); + } + +} diff --git a/parabotv2/src/org/parabot/core/ui/ServerSelector.java b/parabotv2/src/org/parabot/core/ui/ServerSelector.java new file mode 100644 index 0000000..1d135a9 --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/ServerSelector.java @@ -0,0 +1,59 @@ +package org.parabot.core.ui; + +import java.awt.Dimension; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import org.parabot.core.ui.utils.Center; +import org.parabot.core.ui.widgets.ServerWidget; + + +public class ServerSelector extends JFrame +{ + private static final long serialVersionUID = 1L; + private static ServerSelector instance = null; + + public static ServerSelector getInstance() { + if(instance != null) { + instance.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + return instance; + } + return instance = new ServerSelector(); + } + + + public ServerSelector() { + setLayout(null); + ServerWidget[] widgets = getServers(); + JPanel p = new JPanel(); + p.setBounds(0, 0, 400, 800); + p.setLayout(null); + p.setPreferredSize(new Dimension(400, widgets.length * 100)); + JScrollPane pane = new JScrollPane(p); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + for (int i = 0; i < widgets.length; i++) { + widgets[i].setBounds(0, i * 100, 400, 100); + p.add(widgets[i]); + } + pane.setBounds(0, 0, 400, 200); + pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + getContentPane().add(pane); + setResizable(false); + setTitle("Servers"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Center.centerFramea(this, 406, 228); + } + + public ServerWidget[] getServers() { + ArrayList widgets = new ArrayList(); + widgets.add(new ServerWidget("RecklessPk", "Clisprail", "317")); + widgets.add(new ServerWidget("Soulsplit", "Clisprail", "317")); + widgets.add(new ServerWidget("RS2007", "Clisprail", "10")); + return widgets.toArray(new ServerWidget[widgets.size()]); + } + +} + diff --git a/parabotv2/src/org/parabot/core/ui/applet/LoadApplet.java b/parabotv2/src/org/parabot/core/ui/applet/LoadApplet.java new file mode 100644 index 0000000..4c72dac --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/applet/LoadApplet.java @@ -0,0 +1,79 @@ +package org.parabot.core.ui.applet; + +import java.applet.Applet; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +import org.parabot.core.ui.images.Images; + +/** + * An informative applet which tells the user what bot is doing + * + * @author Clisprail + * + */ +public class LoadApplet extends Applet { + private static final long serialVersionUID = 7412412644921803896L; + private static LoadApplet current = null; + private static String state = "Initializing loader..."; + private FontMetrics fontMetrics = null; + private BufferedImage bot_image = null; + + public LoadApplet() { + bot_image = Images.getResource("/org/parabot/core/ui/images/para.png"); + } + + @Override + public void init() { + setSize(775, 510); + setBackground(Color.black); + } + + /** + * Paints on this applet + */ + @Override + public void paint(Graphics g) { + g.setFont(new Font("Calibri", Font.PLAIN, 18)); + if (fontMetrics == null) { + fontMetrics = g.getFontMetrics(); + } + g.setColor(Color.white); + int x = (getWidth() / 2) - (fontMetrics.stringWidth(state) / 2); + g.drawString(state, x, 200); + g.setFont(new Font("Calibri", Font.PLAIN, 12)); + final String version = "v2.0"; + g.drawString(version, + getWidth() - g.getFontMetrics().stringWidth(version) - 10, + getHeight() - 12); + x = (getWidth() / 2) - (bot_image.getWidth() / 2); + g.drawImage(bot_image, x, 80, null); + } + + /** + * Gets instance of this applet + * @return instance of this applet + */ + public static LoadApplet get() { + if (current != null) { + return current; + } + final LoadApplet splash = new LoadApplet(); + splash.init(); + current = splash; + return current; + } + + /** + * Updates the status message and repaints the applet + * @param message + */ + public static void setState(final String message) { + state = message; + current.repaint(); + } + +} diff --git a/parabotv2/src/org/parabot/core/ui/components/BotToolbar.java b/parabotv2/src/org/parabot/core/ui/components/BotToolbar.java new file mode 100644 index 0000000..d3a943a --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/components/BotToolbar.java @@ -0,0 +1,160 @@ +package org.parabot.core.ui.components; + +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; + +import org.parabot.core.Context; +import org.parabot.core.ui.ServerSelector; +import org.parabot.core.ui.images.Images; + +/** + * Bot toolbar + * + * @author Clisprail + * + */ +public class BotToolbar extends JToolBar { + private static final long serialVersionUID = 5373484845104212180L; + private static BotToolbar instance = null; + private JButton tab = null; + private static Map environments = new HashMap(); + + public BotToolbar() { + setFloatable(false); + final JButton run = new JButton(); + final JButton pause = new JButton(); + tab = new JButton(); + tab.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + ServerSelector.getInstance().setVisible(true); + } + + }); + run.setFocusable(false); + pause.setFocusable(false); + tab.setFocusable(false); + try { + run.setIcon(new ImageIcon(Images.getResource("/org/parabot/core/ui/images/run.png"))); + pause.setIcon(new ImageIcon(Images.getResource("/org/parabot/core/ui/images/pause.png"))); + tab.setIcon(new ImageIcon(Images.getResource("/org/parabot/core/ui/images/add.png"))); + } catch (Throwable t) { + t.printStackTrace(); + } + //add(tab); + add(Box.createHorizontalGlue()); + add(run); + add(pause); + } + + public static BotToolbar getInstance() { + return instance == null ? instance = new BotToolbar() : instance; + } + + public void addTab(final Context context, final String name) { + TabButton b = new TabButton(name); + b.setActive(true); + b.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + final TabButton tabButton = (TabButton) e.getSource(); + final Context context = environments.get(tabButton); + for(final TabButton button : environments.keySet()) { + button.setActive(false); + } + tabButton.setActive(true); + if(!context.appletSet()) { + return; + } + GamePanel.getInstance().setContext(context); + } + + }); + //add(b, getComponentIndex(tab)); + add(b, 0); + environments.put(b, context); + } + + + /** + * Tab button + * @author Clisprail + * + */ + private final class TabButton extends JButton { + private static final long serialVersionUID = 1L; + + public TabButton(final String name) { + super(name); + setFocusable(false); + setHorizontalTextPosition(SwingConstants.LEFT); + setIcon(getBlackClose()); + final Component c = this; + addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(final MouseEvent e) { + if (e.getX() > getWidth() - getIcon().getIconWidth() + && e.getX() < getWidth() - getIconTextGap()) { + setIcon(getRedClose()); + } else { + setIcon(getBlackClose()); + } + } + }); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + @SuppressWarnings("unused") + final int n = getComponentIndex(c); + if (e.getX() > getWidth() - getIcon().getIconWidth() + && e.getX() < getWidth() - getIconTextGap()) { + // close + } else { + // enable + } + } + + @Override + public void mouseExited(final MouseEvent e) { + setIcon(getBlackClose()); + } + }); + } + + private final ImageIcon getBlackClose() { + try { + return new ImageIcon(Images.getResource("/org/parabot/core/ui/images/close.png")); + } catch (Throwable t) { + throw new RuntimeException("Unable to load icon image: " + t.getMessage()); + } + } + + private final ImageIcon getRedClose() { + try { + return new ImageIcon(Images.getResource("/org/parabot/core/ui/images/close_red.png")); + } catch (Throwable t) { + throw new RuntimeException("Unable to load icon image: " + t.getMessage()); + } + } + + public void setActive(final boolean active) { + setFont(getFont().deriveFont(active ? Font.BOLD : Font.PLAIN)); + } + } + +} diff --git a/parabotv2/src/org/parabot/core/ui/components/GamePanel.java b/parabotv2/src/org/parabot/core/ui/components/GamePanel.java new file mode 100644 index 0000000..792e238 --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/components/GamePanel.java @@ -0,0 +1,89 @@ +package org.parabot.core.ui.components; + +import java.applet.Applet; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import javax.swing.GroupLayout; +import javax.swing.JPanel; + +import org.parabot.core.Context; +import org.parabot.core.ui.applet.LoadApplet; + +/** + * + * Main panel where applets are added. + * + * @author Clisprail + * + * + */ +public class GamePanel extends JPanel { + private static final long serialVersionUID = 1L; + private static GamePanel instance = null; + private static LoadApplet loader = LoadApplet.get(); + public Context context = null; + + private GamePanel() { + setFocusable(true); + setFocusTraversalKeysEnabled(false); + setOpaque(true); + setBackground(Color.black); + setPreferredSize(new Dimension(770, 503)); + GroupLayout panelLayout = new GroupLayout(this); + setLayout(panelLayout); + panelLayout.setHorizontalGroup(panelLayout.createParallelGroup( + GroupLayout.Alignment.LEADING).addGap(0, 770, Short.MAX_VALUE)); + panelLayout.setVerticalGroup(panelLayout.createParallelGroup( + GroupLayout.Alignment.LEADING).addGap(0, 418, Short.MAX_VALUE)); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + } + + /** + * Updates context of this panel and adds a different Applet to the panel + * @param context + */ + public void setContext(final Context c) { + if(context != null) { + context.getApplet().setVisible(false); + } + context = c; + if(c == null) { + return; + } + final Applet gameApplet = context.getApplet(); + if(!c.added) { + add(gameApplet); + c.added = true; + return; + } + gameApplet.setVisible(true); + } + + + /** + * Gets instance of this panel + * @return instance of this panel + */ + public static GamePanel getInstance() { + return instance == null ? instance = new GamePanel() : instance; + } + + /** + * Adds the loader applet + */ + public void addLoader() { + add(loader); + } + + /** + * Removes the loader applet + */ + public void removeLoader() { + remove(loader); + } +} diff --git a/parabotv2/src/org/parabot/core/ui/components/LogArea.java b/parabotv2/src/org/parabot/core/ui/components/LogArea.java new file mode 100644 index 0000000..c5bc621 --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/components/LogArea.java @@ -0,0 +1,83 @@ +package org.parabot.core.ui.components; + + +import java.awt.Dimension; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Properties; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; + +import org.parabot.core.logging.LogFormatter; +import org.parabot.core.logging.LogTextArea; +import org.parabot.core.logging.SystemConsoleHandler; +import org.parabot.core.logging.TextAreaLogHandler; + +public class LogArea extends JScrollPane { + private static final long serialVersionUID = 6571141103751675714L; + private static LogArea instance = null; + private static LogTextArea logArea = new LogTextArea(); + + private LogArea() { + super(TextAreaLogHandler.TEXT_AREA, + ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + logArea.setPreferredSize(new Dimension(775, 80)); + setPreferredSize(new Dimension(776, 100)); + setSize(775, 100); + setVisible(true); + registerLogging(); + } + + public static LogArea getInstance() { + return instance == null ? instance = new LogArea() : instance; + } + + private static final Logger log = Logger.getLogger("Bot"); + + public static void log(String s) { + log.info(s); + } + + public static void error(String s) { + log.severe(s); + } + + public static void clearLog() { + logArea.clearSelection(); + } + + public static void hideLog() { + logArea.setVisible(false); + } + + public static void showLog() { + logArea.setVisible(true); + } + + public void registerLogging() { + final Properties logging = new Properties(); + final String logFormatter = LogFormatter.class.getCanonicalName(); + final String fileHandler = FileHandler.class.getCanonicalName(); + logging.setProperty("handlers", + TextAreaLogHandler.class.getCanonicalName() + "," + fileHandler); + logging.setProperty(".level", "INFO"); + logging.setProperty(SystemConsoleHandler.class.getCanonicalName() + + ".formatter", logFormatter); + logging.setProperty(fileHandler + ".formatter", logFormatter); + logging.setProperty(TextAreaLogHandler.class.getCanonicalName() + + ".formatter", logFormatter); + final ByteArrayOutputStream logout = new ByteArrayOutputStream(); + try { + logging.store(logout, ""); + LogManager.getLogManager().readConfiguration( + new ByteArrayInputStream(logout.toByteArray())); + } catch (final Exception ignored) { + } + } + +} diff --git a/parabotv2/src/org/parabot/core/ui/components/ServerPanel.java b/parabotv2/src/org/parabot/core/ui/components/ServerPanel.java new file mode 100644 index 0000000..0c8e8c4 --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/components/ServerPanel.java @@ -0,0 +1,40 @@ +package org.parabot.core.ui.components; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; + +import javax.swing.GroupLayout; +import javax.swing.JPanel; + +/** + * + * @author Clisprail + * + */ +public class ServerPanel extends JPanel { + private static final long serialVersionUID = 6034139911394898295L; + + + public ServerPanel() { + setBorder(null); + setOpaque(false); + setPreferredSize(new Dimension(765, 503)); + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + layout.setHorizontalGroup(layout.createParallelGroup( + GroupLayout.Alignment.LEADING).addGap(0, 706, Short.MAX_VALUE)); + layout.setVerticalGroup(layout.createParallelGroup( + GroupLayout.Alignment.LEADING).addGap(0, 418, Short.MAX_VALUE)); + } + + + @Override + protected void paintComponent(Graphics g) { + g.setColor(Color.LIGHT_GRAY); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + + +} diff --git a/parabotv2/src/org/parabot/core/ui/images/Images.java b/parabotv2/src/org/parabot/core/ui/images/Images.java new file mode 100644 index 0000000..e968b7f --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/images/Images.java @@ -0,0 +1,24 @@ +package org.parabot.core.ui.images; + +import java.awt.image.BufferedImage; +import java.util.HashMap; + +import javax.imageio.ImageIO; + +public class Images { + private static final HashMap imageCache = new HashMap(); + + public static BufferedImage getResource(final String resource) { + if(imageCache.containsKey(resource)) { + return imageCache.get(resource); + } + try { + final BufferedImage img = ImageIO.read(Images.class.getResourceAsStream(resource)); + imageCache.put(resource, img); + return img; + } catch (Throwable t) { + throw new RuntimeException("Failed to load image from resource. " + t.getMessage()); + } + } + +} diff --git a/parabotv2/src/org/parabot/core/ui/images/add.png b/parabotv2/src/org/parabot/core/ui/images/add.png new file mode 100644 index 0000000..627fc65 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/add.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/close.png b/parabotv2/src/org/parabot/core/ui/images/close.png new file mode 100644 index 0000000..f1ccf5d Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/close.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/close_red.png b/parabotv2/src/org/parabot/core/ui/images/close_red.png new file mode 100644 index 0000000..80afed1 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/close_red.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/icon.png b/parabotv2/src/org/parabot/core/ui/images/icon.png new file mode 100644 index 0000000..37f8e47 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/icon.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/para.png b/parabotv2/src/org/parabot/core/ui/images/para.png new file mode 100644 index 0000000..53facaf Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/para.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/pause.png b/parabotv2/src/org/parabot/core/ui/images/pause.png new file mode 100644 index 0000000..f80b488 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/pause.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/run.png b/parabotv2/src/org/parabot/core/ui/images/run.png new file mode 100644 index 0000000..071f287 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/run.png differ diff --git a/parabotv2/src/org/parabot/core/ui/images/stop.png b/parabotv2/src/org/parabot/core/ui/images/stop.png new file mode 100644 index 0000000..02612d4 Binary files /dev/null and b/parabotv2/src/org/parabot/core/ui/images/stop.png differ diff --git a/parabotv2/src/org/parabot/core/ui/utils/Center.java b/parabotv2/src/org/parabot/core/ui/utils/Center.java new file mode 100644 index 0000000..fac5597 --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/utils/Center.java @@ -0,0 +1,30 @@ +package org.parabot.core.ui.utils; + + +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JFrame; + +/** + * + * @author Clisprail + * + */ +public final class Center { + + /** + * Centers a JFrame + * @param frame + * @param width + * @param height + */ + public static final void centerFramea(final JFrame f, final int width, final int height) { + final Dimension fscreen = Toolkit.getDefaultToolkit().getScreenSize(); + final int x = (fscreen.width - width) / 2; + final int y = (fscreen.height - height) / 2; + f.setBounds(x, y, width, height); + } + +} + diff --git a/parabotv2/src/org/parabot/core/ui/widgets/ServerWidget.java b/parabotv2/src/org/parabot/core/ui/widgets/ServerWidget.java new file mode 100644 index 0000000..809bd6c --- /dev/null +++ b/parabotv2/src/org/parabot/core/ui/widgets/ServerWidget.java @@ -0,0 +1,95 @@ +package org.parabot.core.ui.widgets; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.MalformedURLException; +import java.net.URL; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.parabot.environment.Environment; + +/** + * A colorful server widget + * @author Clisprail + * + */ +public class ServerWidget extends JPanel { + + private static final long serialVersionUID = 1L; + private String name = null; + + final ActionListener play = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + load(new URL("http://parnassian.host56.com/" + name + + ".jar"), name); + } catch (MalformedURLException e1) { + e1.printStackTrace(); + } + } + }; + + public ServerWidget(final String serverName, final String author, + final String revision) { + setLayout(null); + this.name = serverName.replaceAll(" ", ""); + JLabel l = new JLabel(); + l.setFont(new Font("Arial", Font.BOLD, 16)); + l.setForeground(Color.white); + l.setText(serverName); + l.setBounds(10, 10, 100, 20); + add(l); + final Font f = new Font("Arial", Font.PLAIN, 12); + add(l); + l = new JLabel(); + l.setFont(f); + l.setForeground(Color.white); + l.setBounds(10, 45, 100, 20); + l.setText("Author: " + author); + add(l); + l = new JLabel(); + l.setFont(f); + l.setForeground(Color.white); + l.setBounds(10, 60, 100, 20); + l.setText("Revision: " + revision); + add(l); + final JButton b = new JButton("Start"); + b.setFocusable(false); + b.setBounds(300, 70, 70, 20); + b.setOpaque(false); + b.addActionListener(play); + add(b); + } + + @Override + public String getName() { + return name; + } + + @Override + public void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + setOpaque(false); + super.paintComponent(g); + setOpaque(true); + int w = getWidth(); + int h = getHeight(); + Color color1 = new Color(160, 0, 0); + Color color2 = color1.darker(); + GradientPaint gp = new GradientPaint(0, 0, color1, 0, h, color2); + g2d.setPaint(gp); + g2d.fillRect(0, 0, w, h); + } + + public void load(final URL url, final String serverName) { + Environment.load(url, serverName); + } +} diff --git a/parabotv2/src/org/parabot/environment/Environment.java b/parabotv2/src/org/parabot/environment/Environment.java new file mode 100644 index 0000000..105d066 --- /dev/null +++ b/parabotv2/src/org/parabot/environment/Environment.java @@ -0,0 +1,59 @@ +package org.parabot.environment; + +import java.lang.reflect.Constructor; +import java.net.URL; + +import org.parabot.core.Context; +import org.parabot.core.classpath.ClassPath; +import org.parabot.core.ui.BotUI; +import org.parabot.core.ui.ServerSelector; +import org.parabot.core.ui.components.BotToolbar; +import org.parabot.environment.servers.ServerProvider; +import org.parabot.environment.servers.loader.ServerLoader; + +/** + * + * @author Clisprail + * + */ +public class Environment { + + /** + * Loads a new environment + * @param url + */ + public static void load(final URL url, final String serverName) { + ServerSelector.getInstance().dispose(); + if(!BotUI.getInstance().isVisible()) { + BotUI.getInstance().setVisible(true); + } + + final ClassPath classPath = new ClassPath(); + classPath.addJar(url.toString()); + + final ServerLoader serverLoader = new ServerLoader(classPath); + final String[] serverProviders = serverLoader.getServerClassNames(); + if (serverProviders == null) { + throw new RuntimeException("No server provided."); + } + + final String id = "tab" + Context.getID(); + final ThreadGroup bot = new ThreadGroup(id); + new Thread(bot, new Runnable() { + @Override + public void run() { + try { + final Class serverProviderClass = serverLoader.loadClass(serverProviders[0]); + final Constructor con = serverProviderClass.getConstructor(); + ServerProvider server = (ServerProvider) con.newInstance(); + server.context.setEnvironment(serverLoader); + BotToolbar.getInstance().addTab(server.context, serverName); + server.context.load(); + } catch (Throwable t) { + throw new RuntimeException("Error while loading server. " + t.getMessage()); + } + } + }).start(); + } + +} diff --git a/parabotv2/src/org/parabot/environment/api/utils/Filter.java b/parabotv2/src/org/parabot/environment/api/utils/Filter.java new file mode 100644 index 0000000..e819fc2 --- /dev/null +++ b/parabotv2/src/org/parabot/environment/api/utils/Filter.java @@ -0,0 +1,14 @@ +package org.parabot.environment.api.utils; + +/** + * A simple class to filter things + * + * @author Clisprail + * + * @param + */ +public interface Filter +{ + public boolean accept(F f); +} + diff --git a/parabotv2/src/org/parabot/environment/servers/ServerManifest.java b/parabotv2/src/org/parabot/environment/servers/ServerManifest.java new file mode 100644 index 0000000..e1e143a --- /dev/null +++ b/parabotv2/src/org/parabot/environment/servers/ServerManifest.java @@ -0,0 +1,22 @@ +package org.parabot.environment.servers; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A server manifest + * @author Clisprail + * + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ServerManifest { + + String author(); + + String name(); + + double version(); + + Type type(); + +} diff --git a/parabotv2/src/org/parabot/environment/servers/ServerProvider.java b/parabotv2/src/org/parabot/environment/servers/ServerProvider.java new file mode 100644 index 0000000..bfd158c --- /dev/null +++ b/parabotv2/src/org/parabot/environment/servers/ServerProvider.java @@ -0,0 +1,53 @@ +package org.parabot.environment.servers; + +import java.applet.Applet; +import java.applet.AppletStub; +import java.net.URL; +import javax.swing.JMenuBar; + +import org.parabot.core.Context; +/** + * Provides a server to the bot + * + * @author Clisprail + * + */ +public abstract class ServerProvider { + public Context context = new Context(this); + + /** + * Hooks to parse + * @return URL to hooks file + */ + public URL getHooks() { + return null; + } + + /** + * Jar to parse + * @return URL to client jar + */ + public abstract String getJar(); + + public abstract Applet fetchApplet(); + + public String getAccessorsPackage() { + return null; + } + + /** + * Add custom items to the bot menu bar + * @param menu bar to add items on + */ + public void addMenuItems(JMenuBar bar) { + } + + public AppletStub getStub() { + return null; + } + + public void parseJar() { + context.getClassPath().addJar(getJar()); + } + +} diff --git a/parabotv2/src/org/parabot/environment/servers/Type.java b/parabotv2/src/org/parabot/environment/servers/Type.java new file mode 100644 index 0000000..ca76c7c --- /dev/null +++ b/parabotv2/src/org/parabot/environment/servers/Type.java @@ -0,0 +1,14 @@ +package org.parabot.environment.servers; + +/** + * + * Bot type + * + * @author Clisprail + * + */ +public enum Type { + + INJECTION, REFLECTION, COLOR, OTHER + +} diff --git a/parabotv2/src/org/parabot/environment/servers/loader/ServerLoader.java b/parabotv2/src/org/parabot/environment/servers/loader/ServerLoader.java new file mode 100644 index 0000000..ee4c137 --- /dev/null +++ b/parabotv2/src/org/parabot/environment/servers/loader/ServerLoader.java @@ -0,0 +1,40 @@ +package org.parabot.environment.servers.loader; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.tree.ClassNode; +import org.parabot.core.asm.ASMClassLoader; +import org.parabot.core.classpath.ClassPath; +import org.parabot.environment.servers.ServerProvider; + +/** + * + * @author Clisprail + * + * + */ +public class ServerLoader extends ASMClassLoader { + private ClassPath classPath = null; + + public ServerLoader(ClassPath classPath) { + super(classPath); + this.classPath = classPath; + } + + + /** + * Gets all classes that extends ServerProvider + * @return string array of class names that extends ServerProvider + */ + public final String[] getServerClassNames() { + final List classNames = new ArrayList(); + for (ClassNode c : classPath.classes.values()) + if (c.superName.replace('/', '.').equals( + ServerProvider.class.getName())) { + classNames.add(c.name.replace('/', '.')); + } + return classNames.toArray(new String[classNames.size()]); + } + +}