diff --git a/src/main/java/org/rebotted/Client.java b/src/main/java/org/rebotted/Client.java index 949465c..de98863 100644 --- a/src/main/java/org/rebotted/Client.java +++ b/src/main/java/org/rebotted/Client.java @@ -1,5 +1,10 @@ package org.rebotted; +import org.rebotted.archive.ASMClassLoader; +import org.rebotted.archive.ClassArchive; +import org.rebotted.bot.data.APIData; +import org.rebotted.bot.data.RebottedAPI; +import org.rebotted.bot.loader.APILoader; import org.rebotted.cache.FileArchive; import org.rebotted.cache.FileStore; import org.rebotted.cache.FileStore.Store; @@ -547,6 +552,9 @@ public class Client extends GameApplet { private int anInt1285; private String selectedItemName; private int publicChatMode; + private ClassArchive classArchive; + private RebottedAPI api; + private APIData apiData; public Client() { xpAddedPos = xpCounter = expAdded = 0; fullscreenInterfaceID = -1; @@ -883,29 +891,28 @@ public class Client extends GameApplet { ObjectDefinition.lowMemory = true; } - - - public static void main(String[] args) { - SwingUtilities.invokeLater(() -> { - try { - JFrame.setDefaultLookAndFeelDecorated(true); - JPopupMenu.setDefaultLightWeightPopupEnabled(false); - ToolTipManager toolTipManager = ToolTipManager.sharedInstance(); - toolTipManager.setLightWeightPopupEnabled(false); - UIManager.setLookAndFeel(new SubstanceDark()); - nodeID = 10; - portOffset = 0; - setHighMem(); - isMembers = true; - SignLink.storeid = 32; - SignLink.startpriv(InetAddress.getLocalHost()); - frameMode(ScreenMode.FIXED); - instance = new Client(); - botFrame = new BotFrame(instance, false); - } catch (Exception e) { - e.printStackTrace(); - } - }); + public static void initFrame() { + try { + nodeID = 10; + portOffset = 0; + setHighMem(); + isMembers = true; + SignLink.storeid = 32; + SignLink.startpriv(InetAddress.getLocalHost()); + frameMode(Client.ScreenMode.FIXED); + instance = new Client(); + System.out.println("Attempting to download latest bot API."); + final APILoader apiLoader = new APILoader(instance, "https://parabot-osrs.000webhostapp.com/RebottedAPI.jar"); + if(apiLoader.getApiData() == null) { + System.err.println("There was a error downloading the API!"); + System.exit(0); + } + instance.apiData = apiLoader.getApiData(); + instance.api = apiLoader.getRebottedAPI(); + botFrame = new BotFrame(instance, false); + } catch (Exception e) { + e.printStackTrace(); + } } public static void setTab(int id) { @@ -13872,6 +13879,14 @@ public class Client extends GameApplet { this.anInt1187 += (j << 1); } + public RebottedAPI getApi() { + return api; + } + + public APIData getApiData() { + return apiData; + } + public enum ScreenMode { FIXED, RESIZABLE, FULLSCREEN } diff --git a/src/main/java/org/rebotted/Core.java b/src/main/java/org/rebotted/Core.java new file mode 100644 index 0000000..69f3149 --- /dev/null +++ b/src/main/java/org/rebotted/Core.java @@ -0,0 +1,28 @@ +package org.rebotted; + +import org.rebotted.bot.data.RebottedAPI; +import org.rebotted.bot.loader.APILoader; +import org.rebotted.directory.DirectoryManager; +import org.rebotted.ui.themes.SubstanceDark; + +import javax.swing.*; + +public class Core { + public static void main(String[] args) throws IllegalAccessException, InstantiationException { + DirectoryManager.init(); + SwingUtilities.invokeLater(() -> { + try { + System.setProperty("insubstantial.checkEDT", "false"); //turns off printing Substance throwables. + System.setProperty("insubstantial.logEDT", "false"); //turns off printing Substance exceptions. + JFrame.setDefaultLookAndFeelDecorated(true); + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + ToolTipManager toolTipManager = ToolTipManager.sharedInstance(); + toolTipManager.setLightWeightPopupEnabled(false); + UIManager.setLookAndFeel(new SubstanceDark()); + Client.initFrame(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } +} diff --git a/src/main/java/org/rebotted/archive/ASMClassLoader.java b/src/main/java/org/rebotted/archive/ASMClassLoader.java new file mode 100644 index 0000000..08055ce --- /dev/null +++ b/src/main/java/org/rebotted/archive/ASMClassLoader.java @@ -0,0 +1,123 @@ +package org.rebotted.archive; + + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.tree.ClassNode; + +import java.net.MalformedURLException; +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; + +public class ASMClassLoader extends ClassLoader { + + private final Map> classCache; + private final ClassArchive classArchive; + + public ASMClassLoader(final ClassArchive classArchive) { + this.classCache = new HashMap<>(); + this.classArchive = classArchive; + } + + + @Override + protected URL findResource(String name) { + if (getSystemResource(name) == null) { + if (classArchive.resources.containsKey(name)) { + try { + return classArchive.resources.get(name).toURI().toURL(); + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } else { + return null; + } + } + return getSystemResource(name); + } + + public void inheritClassLoader(ASMClassLoader classLoader) { + if (classLoader == null) + return; + inheritClassCache(classLoader); + } + + private void inheritClassCache(ASMClassLoader classLoader) { + for (Map.Entry> classNodes : classLoader.classCache.entrySet()) { + if (classCache.containsKey(classNodes.getKey())) { + classCache.remove(classNodes.getKey()); + classCache.put(classNodes.getKey(), classNodes.getValue()); + } else { + classCache.put(classNodes.getKey(), classNodes.getValue()); + } + } + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return findClass(name); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + try { + return getSystemClassLoader().loadClass(name); + } catch (Exception ignored) { + + } + final String key = name.replace('.', '/'); + if (classCache.containsKey(key)) { + return classCache.get(key); + } + final ClassNode node = classArchive.classes.get(key); + if (node != null) { + final Class c = nodeToClass(node); + classCache.put(key, c); + return c; + } + return getSystemClassLoader().loadClass(name); + } + + private final Class nodeToClass(ClassNode node) { + if (super.findLoadedClass(node.name) != null) { + return findLoadedClass(node.name); + } + final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + node.accept(cw); + final byte[] b = cw.toByteArray(); + if (classArchive.classesBytes.containsKey(node.name + ".class")) { + classArchive.classesBytes.remove(node.name + ".class"); + classArchive.classesBytes.put(node.name + ".class", b); + } + return defineClass(node.name.replace('/', '.'), b, 0, b.length, + getDomain()); + } + + private final ProtectionDomain getDomain() { + CodeSource code = null; + try { + code = new CodeSource(new URL("http://127.0.0.1"), (Certificate[]) null); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return new ProtectionDomain(code, getPermissions()); + } + + private final Permissions getPermissions() { + final Permissions permissions = new Permissions(); + permissions.add(new AllPermission()); + return permissions; + } + + public ClassArchive getClassArchive() { + return classArchive; + } + +} + diff --git a/src/main/java/org/rebotted/archive/ClassArchive.java b/src/main/java/org/rebotted/archive/ClassArchive.java new file mode 100644 index 0000000..b83ea8b --- /dev/null +++ b/src/main/java/org/rebotted/archive/ClassArchive.java @@ -0,0 +1,188 @@ +package org.rebotted.archive; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import org.rebotted.directory.DirectoryManager; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ClassArchive { + public final ArrayList classNames; + public final Map classes; + public final Map classesBytes; + public final Map resourcesBytes; + public final Map resources; + + public ClassArchive() { + this.classNames = new ArrayList<>(); + this.classes = new HashMap<>(); + this.resources = new HashMap<>(); + this.classesBytes = new HashMap<>(); + this.resourcesBytes = new HashMap<>(); + + } + + public void inheritClassArchive(ClassArchive classArchive) { + if (classArchive == null) + return; + inheritClassNodeCache(classArchive); + inheritClassNames(classArchive); + inheritResourceCache(classArchive); + } + + private void inheritClassNodeCache(ClassArchive classArchive) { + for (Map.Entry classNodes : classArchive.classes.entrySet()) { + if (classes.containsKey(classNodes.getKey())) { + classes.remove(classNodes.getKey()); + classes.put(classNodes.getKey(), classNodes.getValue()); + } else { + classes.put(classNodes.getKey(), classNodes.getValue()); + } + } + } + + private void inheritResourceCache(ClassArchive classArchive) { + for (Map.Entry resource : classArchive.resources.entrySet()) { + if (resources.containsKey(resource.getKey())) { + resources.remove(resource.getKey()); + resources.put(resource.getKey(), resource.getValue()); + } else { + resources.put(resource.getKey(), resource.getValue()); + } + } + } + + private void inheritClassNames(ClassArchive classArchive) { + for (String s : classArchive.classNames) { + if (!classNames.contains(s)) { + classNames.add(s); + } + } + } + + public void loadClasses(Map classesBytes) { + for (Map.Entry entry : classesBytes.entrySet()) { + final ClassReader cr = new ClassReader(entry.getValue()); + final ClassNode cn = new ClassNode(); + cr.accept(cn, ClassReader.EXPAND_FRAMES); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cn.accept(cw); + final byte[] b = cw.toByteArray(); + classesBytes.put(cn.name + ".class", b); + if (!classNames.contains(cn.name.replace('/', '.'))) { + classNames.add(cn.name.replace('/', '.')); + } + classes.remove(cn.name); + classes.put(cn.name, cn); + } + } + + public void loadResources(Map resourcesBytes) { + for (Map.Entry entry : resourcesBytes.entrySet()) { + try { + String path; + path = DirectoryManager.TEMP_PATH + File.separator; + File f1 = new File(path); + + final File f = File.createTempFile("bot", ".tmp", f1); + f.deleteOnExit(); + try (FileOutputStream fileOuputStream = new FileOutputStream(f)) { + fileOuputStream.write(entry.getValue()); + } catch (IOException e) { + e.printStackTrace(); + } + resources.put(entry.getKey(), f); + resourcesBytes.put(entry.getKey(), entry.getValue()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + protected void loadClass(InputStream in) throws IOException { + final ClassReader cr = new ClassReader(in); + final ClassNode cn = new ClassNode(); + cr.accept(cn, ClassReader.EXPAND_FRAMES); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cn.accept(cw); + final byte[] b = cw.toByteArray(); + classesBytes.put(cn.name + ".class", b); + if (!classNames.contains(cn.name.replace('/', '.'))) { + classNames.add(cn.name.replace('/', '.')); + } + classes.remove(cn.name); + classes.put(cn.name, cn); + + } + + + public void loadResource(final String name, final InputStream in) throws IOException { + String path; + path = DirectoryManager.TEMP_PATH + File.separator; + File f1 = new File(path); + + final File f = File.createTempFile("bot", ".tmp", f1); + f.deleteOnExit(); + final byte[] fileContent = Files.readAllBytes(f.toPath()); + 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) { + } + resources.put(name, f); + resourcesBytes.put(name, fileContent); + } + + public void addJar(final File file) { + try { + addJar(file.toURI().toURL()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + private void addJar(final URL url) { + try { + addJar(url.openConnection()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void addJar(final URLConnection connection) { + try { + final ZipInputStream zin = new ZipInputStream(connection.getInputStream()); + ZipEntry e; + while ((e = zin.getNextEntry()) != null) { + if (e.isDirectory()) + continue; + if (e.getName().endsWith(".class")) { + + loadClass(zin); + } else { + loadResource(e.getName(), zin); + } + } + zin.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public Map getClasses() { + return classes; + } +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/bot/data/APIData.java b/src/main/java/org/rebotted/bot/data/APIData.java new file mode 100644 index 0000000..8d6d715 --- /dev/null +++ b/src/main/java/org/rebotted/bot/data/APIData.java @@ -0,0 +1,46 @@ +package org.rebotted.bot.data; + +import org.rebotted.archive.ClassArchive; + +import java.io.File; + +public class APIData { + + private final Class clazz; + private final double version; + private final File apiPath; + private final ClassLoader classLoader; + private final ClassArchive classArchive; + + public APIData(Class clazz, double version, File apiPath, ClassArchive classArchive, ClassLoader classLoader) { + this.clazz = clazz; + this.version = version; + this.apiPath = apiPath; + this.classArchive = classArchive; + this.classLoader = classLoader; + } + + public Class getMainClass() { + return clazz; + } + + public Class getClazz() { + return clazz; + } + + public double getVersion() { + return version; + } + + public File getApiPath() { + return apiPath; + } + + public ClassLoader getClassLoader() { + return classLoader; + } + + public ClassArchive getClassArchive() { + return classArchive; + } +} diff --git a/src/main/java/org/rebotted/bot/data/APIManifest.java b/src/main/java/org/rebotted/bot/data/APIManifest.java new file mode 100644 index 0000000..12f09d7 --- /dev/null +++ b/src/main/java/org/rebotted/bot/data/APIManifest.java @@ -0,0 +1,10 @@ +package org.rebotted.bot.data; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface APIManifest { + + double version() default 1.0; + +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/bot/data/RebottedAPI.java b/src/main/java/org/rebotted/bot/data/RebottedAPI.java new file mode 100644 index 0000000..75f56bd --- /dev/null +++ b/src/main/java/org/rebotted/bot/data/RebottedAPI.java @@ -0,0 +1,7 @@ +package org.rebotted.bot.data; + +public abstract class RebottedAPI { + + public abstract boolean onStartUp(); + +} diff --git a/src/main/java/org/rebotted/bot/loader/APILoader.java b/src/main/java/org/rebotted/bot/loader/APILoader.java new file mode 100644 index 0000000..079e768 --- /dev/null +++ b/src/main/java/org/rebotted/bot/loader/APILoader.java @@ -0,0 +1,109 @@ +package org.rebotted.bot.loader; + +import jdk.internal.org.objectweb.asm.tree.AnnotationNode; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import org.rebotted.Client; +import org.rebotted.archive.ASMClassLoader; +import org.rebotted.archive.ClassArchive; +import org.rebotted.bot.data.APIData; +import org.rebotted.bot.data.APIManifest; +import org.rebotted.bot.data.RebottedAPI; +import org.rebotted.directory.DirectoryManager; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +public class APILoader { + private final Client client; + private RebottedAPI rebottedAPI; + private APIData apiData; + public APILoader(Client client, String link) { + this.client = client; + this.apiData = downloadAPI(link); + } + + /** + * Testing the loading of the API + * @param args + */ + public static void main(String[] args) { + DirectoryManager.init(); + System.out.println(APIManifest.class.getCanonicalName()); + new APILoader(null, "https://parabot-osrs.000webhostapp.com/RebottedAPI.jar"); + } + + private APIData downloadAPI(String link) { + try { + byte[] bytes = getByteArray(link); + final Map classMap = new HashMap<>(); + final Map resourceMap = new HashMap<>(); + final byte[] array = new byte[1024]; + final JarInputStream jarInputStream = new JarInputStream(new ByteArrayInputStream(bytes)); + ZipEntry nextEntry; + while ((nextEntry = jarInputStream.getNextEntry()) != null) { + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarInputStream jarInputStream2 = jarInputStream; + int read; + while ((read = jarInputStream2.read(array, 0, array.length)) != -1) { + jarInputStream2 = jarInputStream; + byteArrayOutputStream.write(array, 0, read); + } + if (nextEntry.getName().endsWith(".class")) { + classMap.put(nextEntry.getName(), byteArrayOutputStream.toByteArray()); + } else { + resourceMap.put(nextEntry.getName(), byteArrayOutputStream.toByteArray()); + } + } + final ClassArchive classArchive = new ClassArchive(); + classArchive.loadResources(resourceMap); + classArchive.loadClasses(classMap); + final ASMClassLoader classLoader = new ASMClassLoader(classArchive); + for(ClassNode node : classArchive.classes.values()) { + for(AnnotationNode annotationNode : node.visibleAnnotations) { + if(annotationNode.desc.equals("L"+APIManifest.class.getCanonicalName().replaceAll("\\.", "/")+";")) { + final Class clazz = classLoader.loadClass(node.name.replaceAll("/", ".")); + final APIManifest manifest = clazz.getAnnotation(APIManifest.class); + rebottedAPI = (RebottedAPI) clazz.getConstructors()[0].newInstance(client); + System.out.println("Rebotted API version "+manifest.version()+" has been loaded..."); + final APIData apiData = new APIData(clazz, manifest.version(), null, classArchive, classLoader); + return apiData; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private byte[] getByteArray(String link) { + try { + final URL url = new URL(link); + final DataInputStream dataInputStream = new DataInputStream(url.openStream()); + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + int read; + while ((read = dataInputStream.read()) != -1) { + byteArrayOutputStream.write(read); + } + dataInputStream.close(); + return byteArrayOutputStream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public RebottedAPI getRebottedAPI() { + return rebottedAPI; + } + + public APIData getApiData() { + return apiData; + } +} diff --git a/src/main/java/org/rebotted/directory/Directory.java b/src/main/java/org/rebotted/directory/Directory.java new file mode 100644 index 0000000..e173968 --- /dev/null +++ b/src/main/java/org/rebotted/directory/Directory.java @@ -0,0 +1,140 @@ +package org.rebotted.directory; + + +import org.rebotted.directory.exceptions.InvalidDirectoryNameException; +import org.rebotted.directory.exceptions.InvalidFileNameException; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.InvalidPathException; +import java.util.ArrayList; +import java.util.Arrays; + +public class Directory { + + private final File directory; + + public Directory(String path) { + this.directory = new File(path); + if (directory.exists()) { + if (!directory.isDirectory()) { + throw new InvalidPathException(path, "The specified path is not a directory."); + } + } + } + + public Directory(File file) { + this(file.getAbsolutePath()); + } + + public boolean create() throws IOException { + return directory.mkdirs(); + } + + public URI toURI() { + return directory.toURI(); + } + + public boolean exists() { + return directory.exists(); + } + + public String getPath() { + return directory.getAbsolutePath(); + } + + public String getName() { + return directory.getName(); + } + + public boolean isSubDirectoryOf(Directory directory) { + return directory.getPath().startsWith(getPath()) && !directory.getPath().equals(getPath()); + } + + public boolean isSubDirectoryOf(String path) { + final Directory subDirectory = new Directory(path); + return isSubDirectoryOf(subDirectory); + } + + public boolean isParentDirectoryOf(Directory directory) { + return directory.getPath().startsWith(getPath()) && !directory.getPath().equals(getPath()); + } + + public boolean isParentDirectoryOf(String name) { + final Directory directory = new Directory(name); + return isParentDirectoryOf(directory); + } + + public boolean createFile(String path) throws IOException { + final File file = new File(path); + return createFile(file); + } + + public boolean createFile(File file) throws IOException { + if (file.exists()) { + return true; + } + return file.createNewFile(); + } + + public boolean createSubDirectory(String name) throws IOException { + final Directory directory = new Directory(getPath() + File.separator + name); + if (isParentDirectoryOf(directory)) { + return directory.create(); + } + return false; + } + + public File[] getAllFiles() { + final ArrayList files = new ArrayList(); + for (File file : directory.listFiles()) { + if (file.isDirectory()) { + final Directory childDirectory = new Directory(file); + files.addAll(Arrays.asList(childDirectory.getAllFiles())); + continue; + } + files.add(file); + } + return files.toArray(new File[files.size()]); + } + + public File[] getFiles() { + final ArrayList files = new ArrayList(); + for (File file : directory.listFiles()) { + if (!file.isDirectory() && file.getParent().equals(getPath())) { + files.add(file); + } + } + return files.toArray(new File[files.size()]); + } + + public File getFile(String name) throws InvalidFileNameException { + for (File file : getFiles()) { + if (file.getName().equals(name)) { + return file; + } + } + throw new InvalidFileNameException(name); + } + + public Directory[] getSubDirectories() { + final ArrayList directories = new ArrayList(); + for (File file : directory.listFiles()) { + if (file.isDirectory() && file.getParent().equals(getPath())) { + directories.add(new Directory(file.getPath())); + } + } + return directories.toArray(new Directory[directories.size()]); + } + + public Directory getSubDirectory(String name) throws InvalidDirectoryNameException { + for (Directory directory : getSubDirectories()) { + if (directory.getName().equals(name)) { + return directory; + } + } + throw new InvalidDirectoryNameException(name); + } + +} diff --git a/src/main/java/org/rebotted/directory/DirectoryManager.java b/src/main/java/org/rebotted/directory/DirectoryManager.java new file mode 100644 index 0000000..f603f7d --- /dev/null +++ b/src/main/java/org/rebotted/directory/DirectoryManager.java @@ -0,0 +1,69 @@ +package org.rebotted.directory; + + +import org.rebotted.directory.exceptions.InvalidDirectoryNameException; + +import java.io.File; +import java.io.IOException; + +public class DirectoryManager { + + public final static String BOT_DIRECTORY_PATH = System.getProperty("user.home") + File.separator + ".2006rebotted_file_system"; + public final static String CACHE = "Cache"; + public final static String SCRIPTS = "Scripts"; + public final static String TEMP = "Temp"; + public final static String SCREENSHOTS = "Screenshots"; + public static String CACHE_PATH; + public static String SCRIPTS_PATH; + public static String TEMP_PATH; + public static String SCREENSHOTS_PATH; + private static DirectoryManager instance; + private final Directory botDirectory; + + private DirectoryManager() { + botDirectory = getRootDirectory(); + try { + validateSubDirectories(); + } catch (InvalidDirectoryNameException | IOException e) { + e.printStackTrace(); + } + } + + public static void init() { + if (instance == null) { + instance = new DirectoryManager(); + } + } + + public static DirectoryManager getInstance() { + return instance; + } + + public Directory getRootDirectory() { + final Directory directory = new Directory(BOT_DIRECTORY_PATH); + if (!directory.exists()) { + try { + if (directory.create()) { + return directory; + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + return directory; + } + + private void validateSubDirectories() throws IOException, InvalidDirectoryNameException { + botDirectory.createSubDirectory(CACHE); + CACHE_PATH = botDirectory.getSubDirectory(CACHE).getPath(); + botDirectory.createSubDirectory(SCRIPTS); + SCRIPTS_PATH = botDirectory.getSubDirectory(SCRIPTS).getPath(); + botDirectory.getSubDirectory(CACHE).createSubDirectory(TEMP); + TEMP_PATH = botDirectory.getSubDirectory(CACHE).getSubDirectory(TEMP).getPath(); + botDirectory.getSubDirectory(CACHE).createSubDirectory(SCREENSHOTS); + SCREENSHOTS_PATH = botDirectory.getSubDirectory(CACHE).getSubDirectory(SCREENSHOTS).getPath(); + System.out.println("Directories validated."); + } + +} diff --git a/src/main/java/org/rebotted/directory/exceptions/InvalidDirectoryNameException.java b/src/main/java/org/rebotted/directory/exceptions/InvalidDirectoryNameException.java new file mode 100644 index 0000000..7dce743 --- /dev/null +++ b/src/main/java/org/rebotted/directory/exceptions/InvalidDirectoryNameException.java @@ -0,0 +1,11 @@ +package org.rebotted.directory.exceptions; + +public class InvalidDirectoryNameException extends InvalidNameException { + + private static final long serialVersionUID = 6214764160978219616L; + + public InvalidDirectoryNameException(String name) { + super(name + " is not a valid directory name."); + } + +} diff --git a/src/main/java/org/rebotted/directory/exceptions/InvalidFileNameException.java b/src/main/java/org/rebotted/directory/exceptions/InvalidFileNameException.java new file mode 100644 index 0000000..e3c1d2a --- /dev/null +++ b/src/main/java/org/rebotted/directory/exceptions/InvalidFileNameException.java @@ -0,0 +1,11 @@ +package org.rebotted.directory.exceptions; + +public class InvalidFileNameException extends InvalidNameException { + + private static final long serialVersionUID = -3076779825506160714L; + + public InvalidFileNameException(String name) { + super(name + " is not a valid file name."); + } + +} diff --git a/src/main/java/org/rebotted/directory/exceptions/InvalidNameException.java b/src/main/java/org/rebotted/directory/exceptions/InvalidNameException.java new file mode 100644 index 0000000..7b7b189 --- /dev/null +++ b/src/main/java/org/rebotted/directory/exceptions/InvalidNameException.java @@ -0,0 +1,11 @@ +package org.rebotted.directory.exceptions; + +public class InvalidNameException extends Exception { + + private static final long serialVersionUID = 4139344206095457028L; + + public InvalidNameException(String exception) { + super(exception); + } + +} diff --git a/src/main/java/org/rebotted/script/ScriptHandler.java b/src/main/java/org/rebotted/script/ScriptHandler.java new file mode 100644 index 0000000..7900814 --- /dev/null +++ b/src/main/java/org/rebotted/script/ScriptHandler.java @@ -0,0 +1,105 @@ +package org.rebotted.script; + +import org.rebotted.script.scriptdata.ScriptData; +import org.rebotted.script.types.Script; +import org.rebotted.util.Condition; + +public class ScriptHandler implements Runnable { + + private static ScriptHandler instance; + private Thread scriptThread; + private Script script; + private ScriptData scriptData; + private volatile State scriptState = State.STOPPED; + private long breakDuration; + + public ScriptHandler() { + instance = this; + } + + public static ScriptHandler getInstance() { + if(instance == null) + instance = new ScriptHandler(); + return instance; + } + + @Override + public void run() { + try { + while (!scriptState.equals(State.STOPPED)) { + if (script == null) { + stop(); + } else if (scriptState.equals(State.PAUSE)) { + Condition.sleep(500); + } else if (scriptState.equals(State.BREAKING)) { + this.script.onBreak(); + Condition.sleep(breakDuration); + } else { + int timeToSleep = script.operate(); + Condition.sleep(timeToSleep); + } + } + } catch (Exception e) { + stop(); + } + } + + public void start(Script script, ScriptData scriptData) { + if (scriptState.equals(State.RUNNING)) { + System.out.println("Why would you try to start a script with one running?"); + return; + } + if (script == null) { + System.out.println("Error starting script."); + return; + } + if (scriptState.equals(State.PAUSE)) { + System.out.println("Script resumed: " + scriptData.getName()); + this.scriptState = State.RUNNING; + return; + } + System.out.println("Script Started: " + scriptData.getName()); + this.scriptState = State.RUNNING; + this.scriptData = scriptData; + this.script = script; + this.scriptThread = new Thread(this); + if (this.script.onStart()) { + this.scriptThread.start(); + } + } + + public void takeBreak(long duration) { + System.out.println(scriptData.getName() + " Breaking for: " + duration); + this.breakDuration = duration; + this.scriptState = State.BREAKING; + } + + public void stop() { + System.out.println("Script Stopped: " + scriptData.getName()); + this.scriptState = State.STOPPED; + this.script.onStop(); + this.scriptThread.interrupt(); + } + + public void pause() { + if (scriptState.equals(State.RUNNING)) { + System.out.println("Script Paused: " + scriptData.getName()); + this.scriptState = State.PAUSE; + } else if (scriptState.equals(State.PAUSE)) { + System.out.println("Script resumed: " + scriptData.getName()); + this.scriptState = State.RUNNING; + } + } + + public State getScriptState() { + return scriptState; + } + + public void setScriptState(State scriptState) { + this.scriptState = scriptState; + } + + public enum State { + RUNNING, BREAKING, PAUSE, STOPPED + } +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/script/loader/ScriptLoader.java b/src/main/java/org/rebotted/script/loader/ScriptLoader.java new file mode 100644 index 0000000..2a998d9 --- /dev/null +++ b/src/main/java/org/rebotted/script/loader/ScriptLoader.java @@ -0,0 +1,58 @@ +package org.rebotted.script.loader; + +import org.rebotted.bot.data.APIData; +import org.rebotted.directory.DirectoryManager; +import org.rebotted.script.scriptdata.ScriptData; +import org.rebotted.script.scriptdata.ScriptManifest; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class ScriptLoader { + private APIData apiData; + + public ScriptLoader(APIData apiData) { + this.apiData = apiData; + } + + private final List scripts = new ArrayList<>(); + + public List getScripts() { + scripts.clear(); + scripts.addAll(loadLocalScripts()); + return scripts; + } + + private final List loadLocalScripts() { + final List scripts = new ArrayList<>(); + try { + for (File file : DirectoryManager.getInstance().getRootDirectory().getSubDirectory(DirectoryManager.SCRIPTS).getFiles()) { + if (file.getAbsolutePath().endsWith(".jar")) { + apiData.getClassArchive().addJar(file); + try (JarInputStream inputStream = new JarInputStream(new FileInputStream(file))) { + JarEntry jarEntry; + while ((jarEntry = inputStream.getNextJarEntry()) != null) { + if (jarEntry.getName().endsWith(".class") && !jarEntry.getName().contains("$")) { + final String classPackage = jarEntry.getName().replace(".class", ""); + final Class clazz = apiData.getClassLoader().loadClass(classPackage.replaceAll("/", ".")); + if (clazz.isAnnotationPresent(ScriptManifest.class)) { + final ScriptManifest manifest = clazz.getAnnotation(ScriptManifest.class); + final ScriptData scriptData = new ScriptData(clazz, manifest.name(), manifest.description(), manifest.version(), manifest.author(), manifest.category(), file); + scripts.add(scriptData); + } + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return scripts; + } + +} diff --git a/src/main/java/org/rebotted/script/scriptdata/ScriptData.java b/src/main/java/org/rebotted/script/scriptdata/ScriptData.java new file mode 100644 index 0000000..e031386 --- /dev/null +++ b/src/main/java/org/rebotted/script/scriptdata/ScriptData.java @@ -0,0 +1,56 @@ +package org.rebotted.script.scriptdata; + +import java.io.File; + +public class ScriptData { + + private final Class clazz; + private final String name, author, desc; + private final double version; + private final SkillCategory skillCategory; + private final int scriptId; + private final File scriptPath; + + public ScriptData(Class clazz, String name, String desc, double version, String author, SkillCategory category, File scriptPath) { + this.clazz = clazz; + this.name = name; + this.desc = desc; + this.version = version; + this.author = author; + this.skillCategory = category; + this.scriptId = -1; + this.scriptPath = scriptPath; + } + + public Class getMainClass() { + return clazz; + } + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + public double getVersion() { + return version; + } + + public String getAuthor() { + return author; + } + + public SkillCategory getSkillCategory() { + return skillCategory; + } + + public int getScriptId() { + return scriptId; + } + + public File getScriptPath() { + return scriptPath; + } +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/script/scriptdata/ScriptManifest.java b/src/main/java/org/rebotted/script/scriptdata/ScriptManifest.java new file mode 100644 index 0000000..6b3328a --- /dev/null +++ b/src/main/java/org/rebotted/script/scriptdata/ScriptManifest.java @@ -0,0 +1,21 @@ +package org.rebotted.script.scriptdata; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +@Retention(RetentionPolicy.RUNTIME) +public @interface ScriptManifest { + + String name(); + + String author(); + + String description() default ""; + + double version() default 1.0; + + SkillCategory category() default SkillCategory.MISC; + + +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/script/scriptdata/SkillCategory.java b/src/main/java/org/rebotted/script/scriptdata/SkillCategory.java new file mode 100644 index 0000000..e7660da --- /dev/null +++ b/src/main/java/org/rebotted/script/scriptdata/SkillCategory.java @@ -0,0 +1,78 @@ +package org.rebotted.script.scriptdata; + +public enum SkillCategory { + + AGILITY("Agility", new String[]{"Agility"}, new int[]{1}), + COMBAT("Combat", new String[]{"Combat", "Fighter", "Killer", "Kill"}, new int[]{2}), + CONSTRUCTION("Construction", new String[]{"Construction"}, new int[]{3}), + COOKING("Cooking", new String[]{"Cooking"}, new int[]{4}), + CRAFTING("Crafting", new String[]{"Crafting"}, new int[]{5}), + FARMING("Farming", new String[]{"Farming"}, new int[]{6}), + FIREMAKING("Firemaking", new String[]{"Firemaking", "FM"}, new int[]{7}), + FISHING("Fishing", new String[]{"Fishing", "Fisher"}, new int[]{8}), + FLETCHING("Fletching", new String[]{"Fletching"}, new int[]{9}), + HERBLORE("Herblore", new String[]{"Herblore"}, new int[]{11}), + HUNTER("Hunter", new String[]{"Hunter"}, new int[]{12}), + MAGIC("Magic", new String[]{"Magic"}, new int[]{13}), + MINING("Mining", new String[]{"Mining", "Ore"}, new int[]{14}), + MINI_GAMES("Mini-games", new String[]{"Mini-games"}, new int[]{15}), + PRAYER("Prayer", new String[]{"Prayer"}, new int[]{17}), + RANGED("Ranged", new String[]{"Ranged", "Range"}, new int[]{18}), + RUNECRAFTING("Runecrafting", new String[]{"Runecrafting", "Runes"}, new int[]{19}), + SLAYER("Slayer", new String[]{"Slayer"}, new int[]{20}), + SMITHING("Smithing", new String[]{"Smithing"}, new int[]{21}), + THEIVING("Thieving", new String[]{"Thieving"}, new int[]{22}), + WOODCUTTING("Woodcutting", new String[]{"Woodcutting,woodcut,chop,chopper"}, new int[]{23}), + MONEY_MAKING("Money Making", new String[]{"Money Making", "Cash"}, new int[]{16}), + MISC("Misc", new String[]{"Misc"}, new int[]{0, 24}); + + private String skill; + private String[] filters; + private int[] ids; + + SkillCategory(String skill, String[] filters, int[] ids) { + this.skill = skill; + this.filters = filters; + this.ids = ids; + } + + public static SkillCategory getCategory(int id) { + for (SkillCategory skillCategory : SkillCategory.values()) { + for (int a : skillCategory.ids) { + if (a == id) { + return skillCategory; + } + } + } + return SkillCategory.MISC; + } + + public static SkillCategory detect(final String name, final String desc) { + for (SkillCategory skillCategory : SkillCategory.values()) { + for (String filter : skillCategory.filters) { + if (name.toLowerCase().contains(filter.toLowerCase()) || desc.toLowerCase().contains(filter.toLowerCase())) { + return skillCategory; + } + } + } + return SkillCategory.MISC; + } + + public String getName() { + return this.skill; + } + + public String[] getFiltersWord() { + return this.filters; + } + + public int[] getIds() { + return ids; + } + + @Override + public String toString() { + return name().charAt(0) + name().substring(1).replaceAll("_", " ").toLowerCase(); + } + +} diff --git a/src/main/java/org/rebotted/script/types/LoopScript.java b/src/main/java/org/rebotted/script/types/LoopScript.java new file mode 100644 index 0000000..f29b678 --- /dev/null +++ b/src/main/java/org/rebotted/script/types/LoopScript.java @@ -0,0 +1,11 @@ +package org.rebotted.script.types; + +public abstract class LoopScript extends Script { + + public abstract int loop(); + + @Override + public final int operate() { + return loop(); + } +} diff --git a/src/main/java/org/rebotted/script/types/Script.java b/src/main/java/org/rebotted/script/types/Script.java new file mode 100644 index 0000000..b3efbb8 --- /dev/null +++ b/src/main/java/org/rebotted/script/types/Script.java @@ -0,0 +1,18 @@ +package org.rebotted.script.types; + +public abstract class Script { + + private final long startTime = System.currentTimeMillis(); + + public abstract boolean onStart(); + + public abstract void onStop(); + + public abstract void onBreak(); + + public abstract int operate(); + + public final long getRuntime() { + return System.currentTimeMillis() - startTime; + } +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/script/types/Task.java b/src/main/java/org/rebotted/script/types/Task.java new file mode 100644 index 0000000..e0409ff --- /dev/null +++ b/src/main/java/org/rebotted/script/types/Task.java @@ -0,0 +1,23 @@ +package org.rebotted.script.types; + +public abstract class Task { + + private final int priority; + + public Task(int priority) { + this.priority = priority; + } + + public Task() { + this.priority = 0; + } + + public abstract boolean activate(); + + public abstract void execute(); + + public final int priority() { + return priority; + } + +} diff --git a/src/main/java/org/rebotted/script/types/TaskScript.java b/src/main/java/org/rebotted/script/types/TaskScript.java new file mode 100644 index 0000000..539949b --- /dev/null +++ b/src/main/java/org/rebotted/script/types/TaskScript.java @@ -0,0 +1,54 @@ +package org.rebotted.script.types; + +import org.rebotted.script.ScriptHandler; + +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +public abstract class TaskScript extends Script implements Comparator { + + private final List tasks = new LinkedList<>(); + + private synchronized Task get() { + try { + for (Task action : tasks) { + if (action != null && action.activate()) { + return action; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public final void provide(List tasks) { + if (tasks.size() > 0) { + for (Task task : tasks) { + this.tasks.add(task); + } + } + } + + @Override + public final int operate() { + try { + final Task action = get(); + if (action != null) { + action.execute(); + return 200; + } + } catch (Exception e) { + ScriptHandler.getInstance().stop(); + e.printStackTrace(); + } + return 0; + } + + @Override + public int compare(Task o1, Task o2) { + return o1.priority() - o2.priority(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/ui/BotFrame.java b/src/main/java/org/rebotted/ui/BotFrame.java index 1bd8ef8..2cef653 100644 --- a/src/main/java/org/rebotted/ui/BotFrame.java +++ b/src/main/java/org/rebotted/ui/BotFrame.java @@ -1,7 +1,10 @@ package org.rebotted.ui; +import org.rebotted.Client; import org.rebotted.Configuration; import org.rebotted.GameApplet; +import org.rebotted.script.ScriptHandler; +import org.rebotted.script.types.Script; import javax.swing.*; import java.awt.*; @@ -12,16 +15,19 @@ public final class BotFrame extends JFrame implements ActionListener { private static final long serialVersionUID = 1L; - private final BotMenuBar botMenuBar; - - public BotFrame(GameApplet applet, boolean resizable) { + private static BotMenuBar botMenuBar; + private final Client client; + private final ScriptUI scriptUI; + public BotFrame(Client client, boolean resizable) { + this.client = client; + final GameApplet applet = client; setTitle(Configuration.CLIENT_NAME); setResizable(resizable); - botMenuBar = new BotMenuBar(this); + BotFrame.botMenuBar = new BotMenuBar(this); setJMenuBar(botMenuBar); add(applet, BorderLayout.CENTER); - setMinimumSize(new Dimension(774, 559)); - setSize(new Dimension(774, 559)); + setMinimumSize(new Dimension(774, 567)); + setSize(new Dimension(774, 567)); pack(); setLocationRelativeTo(getParent()); setLocationRelativeTo(getOwner()); @@ -29,14 +35,46 @@ public final class BotFrame extends JFrame implements ActionListener { requestFocus(); toFront(); applet.initClientFrame(766, 536); + scriptUI = new ScriptUI(client.getApiData()); System.out.println("Client Launched."); } + public static void setPaused() { + botMenuBar.setPausedButtons(false); + botMenuBar.setRunButtons(true); + botMenuBar.setStopButtons(true); + } + + public static void setRunning() { + botMenuBar.setRunButtons(false); + botMenuBar.setPausedButtons(true); + botMenuBar.setStopButtons(true); + } + + public static void setStopped() { + botMenuBar.setStopButtons(false); + botMenuBar.setPausedButtons(false); + botMenuBar.setRunButtons(true); + } + @Override public void actionPerformed(ActionEvent e) { switch (e.getActionCommand().toLowerCase()) { case "run": - System.out.println("run was clicked.."); + if(ScriptHandler.getInstance().getScriptState() == ScriptHandler.State.STOPPED) { + scriptUI.show(); + } else if(ScriptHandler.getInstance().getScriptState() == ScriptHandler.State.PAUSE) { + ScriptHandler.getInstance().setScriptState(ScriptHandler.State.RUNNING); + setRunning(); + } + break; + case "pause": + setPaused(); + ScriptHandler.getInstance().pause(); + break; + case "stop": + setStopped(); + ScriptHandler.getInstance().stop(); break; } } diff --git a/src/main/java/org/rebotted/ui/BotMenuBar.java b/src/main/java/org/rebotted/ui/BotMenuBar.java index 3cb8233..13bceb3 100644 --- a/src/main/java/org/rebotted/ui/BotMenuBar.java +++ b/src/main/java/org/rebotted/ui/BotMenuBar.java @@ -8,7 +8,7 @@ import javax.swing.*; public class BotMenuBar extends JMenuBar { private BotFrame botUI; - private JButton startButton, pauseButton, stopButton; + private static JButton startButton, pauseButton, stopButton; private JMenu file, scripts; private JMenuItem run, pause, stop; @@ -45,11 +45,11 @@ public class BotMenuBar extends JMenuBar { file.add(exit); - /* startButton = createNewButton(new ImageIcon(Images.getResource("/storage/images/run_button.png")), "Run Script", "Run", true); + startButton = createNewButton(new ImageIcon(getClass().getResource("/images/run_button.png")), "Run Script", "Run", true); - pauseButton = createNewButton(new ImageIcon(Images.getResource("/storage/images/pause_button.png")), "Pause Script", "Pause", false); + pauseButton = createNewButton(new ImageIcon(getClass().getResource("/images/pause_button.png")), "Pause Script", "Pause", false); - stopButton = createNewButton(new ImageIcon(Images.getResource("/storage/images/stop_button.png")), "Stop Script", "Stop", false);*/ + stopButton = createNewButton(new ImageIcon(getClass().getResource("/images/stop_button.png")), "Stop Script", "Stop", false); } @@ -77,41 +77,34 @@ public class BotMenuBar extends JMenuBar { add(file); add(scripts); add(Box.createHorizontalGlue()); - /*add(startButton); + add(startButton); add(pauseButton); - add(stopButton);*/ + add(stopButton); } + + public void setPausedButtons(boolean enabled) { + pause.setEnabled(enabled); + pauseButton.setEnabled(enabled); + } + + public void setStopButtons(boolean enabled) { + stopButton.setEnabled(enabled); + stop.setEnabled(enabled); + } + + public void setRunButtons(boolean enabled) { + run.setEnabled(enabled); + startButton.setEnabled(enabled); + } + public JMenu getFile() { return file; } - public JButton getStartButton() { - return startButton; - } - - public JButton getPauseButton() { - return pauseButton; - } - - public JButton getStopButton() { - return stopButton; - } - public JMenu getScripts() { return scripts; } - public JMenuItem getRun() { - return run; - } - - public JMenuItem getPause() { - return pause; - } - - public JMenuItem getStop() { - return stop; - } } \ No newline at end of file diff --git a/src/main/java/org/rebotted/ui/ScriptController.java b/src/main/java/org/rebotted/ui/ScriptController.java new file mode 100644 index 0000000..5b8dd4b --- /dev/null +++ b/src/main/java/org/rebotted/ui/ScriptController.java @@ -0,0 +1,89 @@ +package org.rebotted.ui; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import org.rebotted.script.ScriptHandler; +import org.rebotted.script.scriptdata.ScriptData; +import org.rebotted.script.scriptdata.SkillCategory; +import org.rebotted.script.types.Script; + + +import java.net.URL; +import java.util.ResourceBundle; + +public class ScriptController implements Initializable { + + @FXML + private Button startButton; + + @FXML + private TableView scriptTable; + + @FXML + private TableColumn category; + + @FXML + private TableColumn scriptName; + + @FXML + private TableColumn author; + + @FXML + private TableColumn description; + + @FXML + private TableColumn version; + + public void startScript() { + if (ScriptHandler.getInstance().getScriptState() == ScriptHandler.State.PAUSE) { + ScriptHandler.getInstance().pause(); + } else if (ScriptHandler.getInstance().getScriptState() == ScriptHandler.State.STOPPED) { + final ScriptData scriptData = scriptTable.getSelectionModel().getSelectedItem(); + if(scriptData == null) { + System.err.println("Please select a script before pressing start!"); + return; + } + startScript(scriptData); + ScriptUI.hide(); + BotFrame.setRunning(); + } else if (ScriptHandler.getInstance().getScriptState() == ScriptHandler.State.RUNNING) { + System.out.println("You already have a script running!"); + } + } + + public void searchScripts() { + + } + + public TableView getScriptTable() { + return scriptTable; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + category.setCellValueFactory(new PropertyValueFactory<>("skillCategory")); + + scriptName.setCellValueFactory(new PropertyValueFactory<>("name")); + + author.setCellValueFactory(new PropertyValueFactory<>("author")); + + description.setCellValueFactory(new PropertyValueFactory<>("desc")); + + version.setCellValueFactory(new PropertyValueFactory<>("version")); + + } + + private void startScript(ScriptData scriptData) { + Script script = null; + try { + script = (Script) scriptData.getMainClass().newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + ScriptHandler.getInstance().start(script, scriptData); + } +} diff --git a/src/main/java/org/rebotted/ui/ScriptUI.java b/src/main/java/org/rebotted/ui/ScriptUI.java new file mode 100644 index 0000000..9347df4 --- /dev/null +++ b/src/main/java/org/rebotted/ui/ScriptUI.java @@ -0,0 +1,74 @@ +package org.rebotted.ui; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.embed.swing.JFXPanel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import org.rebotted.bot.data.APIData; +import org.rebotted.script.loader.ScriptLoader; +import org.rebotted.script.scriptdata.ScriptData; + + +import javax.swing.*; + + +public class ScriptUI { + private static ScriptController controller; + private static JFrame frame; + private JFXPanel jfxPanel = new JFXPanel(); + private ScriptLoader scriptLoader; + + public ScriptUI(final APIData apiData) { + scriptLoader = new ScriptLoader(apiData); + loadUI(); + } + + public static ScriptController getController() { + return controller; + } + + public void loadUI() { + Platform.runLater(() -> { + try { + frame = new JFrame("2006Rebotted - Script Selector"); + jfxPanel = new JFXPanel(); + final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ScriptUI.fxml")); + final Parent root = fxmlLoader.load(); + final Scene scene = new Scene(root, 597, 353); + scene.getStylesheets().add(getClass().getResource("/dark.css").toExternalForm()); + jfxPanel.setScene(scene); + controller = fxmlLoader.getController(); + controller.getScriptTable().setItems(loadLocalScripts()); + SwingUtilities.invokeLater(() -> { + frame.add(jfxPanel); + frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + frame.pack(); + frame.setResizable(false); + }); + } catch (Exception e) { + + } + }); + } + + public void show() { + controller.getScriptTable().setItems(loadLocalScripts()); + frame.setVisible(true); + System.out.println("Script Selector Loaded."); + } + + public static void hide() { + frame.setVisible(false); + } + + private ObservableList loadLocalScripts() { + final ObservableList scripts = FXCollections.observableArrayList(); + for (ScriptData scriptData : scriptLoader.getScripts()) { + scripts.add(scriptData); + } + return scripts; + } +} diff --git a/src/main/java/org/rebotted/util/Condition.java b/src/main/java/org/rebotted/util/Condition.java new file mode 100644 index 0000000..d4ccf10 --- /dev/null +++ b/src/main/java/org/rebotted/util/Condition.java @@ -0,0 +1,134 @@ +package org.rebotted.util; + +import java.nio.file.DirectoryStream.Filter; +import java.util.concurrent.Callable; + +/** + * Condition An event-driven blocking utility. Frequencies are randomly adjusted + * to provide a basic antipattern. + */ +public class Condition { + /** + * The random adjustment variance, 0.85-1.50. + */ + public static final double[] VARIANCE = {.85d, 1.5d}; + + /** + * Blocks until the specified condition is satisfied (returns {@code true}). + * This uses a frequency of 600ms for up to 10 tries, i.e. attempting a + * maximum of 6 seconds. + * + * @param cond the condition + * @return {@code true} if the condition was satisfied, otherwise + * {@code false} + */ + public static boolean wait(final Callable cond) { + return wait(cond, 600, 10); + } + + /** + * Blocks until the specified condition is satisfied (returns {@code true}). + * This uses the specified frequency interval and retries for up to 6 + * seconds. + * + * @param cond the condition + * @param freq the polling frequency in milliseconds + * @return {@code true} if the condition was satisfied, otherwise + * {@code false} + */ + public static boolean wait(final Callable cond, final int freq) { + return wait(cond, freq, Math.max(2, 6000 / freq)); + } + + /** + * Blocks until the specified condition is satisfied (returns {@code true}). + * + * @param cond the condition + * @param freq the polling frequency in milliseconds + * @param tries the maximum number of attempts before this method returns + * {@code false} + * @return if the condition was satisfied, otherwise {@code false} + */ + public static boolean wait(final Callable cond, final int freq, int tries) { + tries = Math.max(1, tries + Random.nextInt(-1, 2)); + + for (int i = 0; i < tries; i++) { + try { + final double f = freq * Random.nextDouble(VARIANCE[0], VARIANCE[1]); + Thread.sleep(Math.max(5, (int) f)); + } catch (final InterruptedException ignored) { + return false; + } + + final boolean r; + try { + r = cond.call(); + } catch (final Exception ignored) { + return false; + } + if (r) { + return true; + } + } + + return false; + } + + /** + * Sleeps the current thread. + * + * @param ms the length of time to sleep in milliseconds, which is adjusted + * by a random variance + * @return the actual amount of time slept in milliseconds, which is subject + * to system clock accuracy + */ + public static int sleep(final long ms) { + if (ms <= 0) { + Thread.yield(); + return 0; + } + final long s = System.nanoTime(); + try { + Thread.sleep((long) (ms * Random.nextDouble(VARIANCE[0], VARIANCE[1]))); + } catch (final InterruptedException ignored) { + } + return (int) ((System.nanoTime() - s) / 1000000L); + } + + /** + * Sleeps the current thread for a duration that is 10 times the value of + * {@link Random#getDelay()}. + */ + public static void sleep() { + sleep(Random.getDelay() * 10); + } + + /** + * Check A simplified conditional checking task. + */ + public static abstract class Check implements Callable, Filter { + /** + * {@inheritDoc} + */ + @Override + public Boolean call() { + return poll(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean accept(final Void v) { + return poll(); + } + + /** + * Checks if a condition has been met. + * + * @return {@code true} if the condition has been met, otherwise + * {@code false} to try again later. + */ + public abstract boolean poll(); + } +} \ No newline at end of file diff --git a/src/main/java/org/rebotted/util/Random.java b/src/main/java/org/rebotted/util/Random.java new file mode 100644 index 0000000..67a8d7f --- /dev/null +++ b/src/main/java/org/rebotted/util/Random.java @@ -0,0 +1,69 @@ +package org.rebotted.util; + +import java.security.SecureRandom; + +public class Random { + private static final double[] pd; + + private static final ThreadLocal random = new ThreadLocal() { + @Override + protected java.util.Random initialValue() { + java.util.Random r; + try { + r = SecureRandom.getInstance("SHA1PRNG", "SUN"); + } catch (Exception ignored) { + r = new java.util.Random(); + } + r.setSeed(r.nextLong()); + return r; + } + }; + + static { + pd = new double[2]; + final double[] e = {3d, 45d + random.get().nextInt(11), 12d + random.get().nextGaussian()}; + final double[] x = {Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().maxMemory() >> 30}; + pd[0] = 4d * Math.log(Math.sin(((Math.PI / x[0]) * Math.PI + 1d) / 4d)) / Math.PI + + 2d * Math.PI * (Math.PI / x[0]) / 3d - 4d * Math.log(Math.sin(0.25d)) / Math.PI; + pd[0] = e[0] * Math.exp(Math.pow(pd[0], 0.75d)) + e[1]; + pd[1] = e[2] * Math.exp(1d / Math.cosh(x[1])); + } + + public static int getDelay() { + return (int) ((-1 + 2 * nextDouble()) * pd[1] + pd[0]); + } + + public static int hicks(final int a) { + return 105 * (int) (Math.log(a * 2) / 0.6931471805599453d); + } + + public static boolean nextBoolean() { + return random.get().nextBoolean(); + } + + public static int nextInt(final int min, final int max) { + final int a = min < max ? min : max, b = max > min ? max : min; + return a + (b == a ? 0 : random.get().nextInt(b - a)); + } + + public static double nextDouble(final double min, final double max) { + final double a = min < max ? min : max, b = max > min ? max : min; + return a + random.get().nextDouble() * (b - a); + } + + public static double nextDouble() { + return random.get().nextDouble(); + } + + public static double nextGaussian() { + return random.get().nextGaussian(); + } + + public static int nextGaussian(final int min, final int max, final double sd) { + return nextGaussian(min, max, (max - min) / 2, sd); + } + + public static int nextGaussian(final int min, final int max, final int mean, final double sd) { + return min + Math.abs(((int) (nextGaussian() * sd + mean)) % (max - min)); + } +} \ No newline at end of file diff --git a/src/main/resources/ScriptUI.fxml b/src/main/resources/ScriptUI.fxml new file mode 100644 index 0000000..d0d4f27 --- /dev/null +++ b/src/main/resources/ScriptUI.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + +