• Welcome to Valhalla Legends Archive.
 

Adding plugin support

Started by Tuberload, June 15, 2004, 05:18 PM

Previous topic - Next topic

Tuberload

This is just a quick example to start off with demonstrating how to dynamically load and use Java class files.

PluginLoader.java:
package tuberload.phuzionbot;

public class PluginLoader
{
   public static void main (String[] args)
   {
      PluginLoader pl = new PluginLoader();
      
      pl.run();
   }
   
   public void run()
   {
      try
      {
         Class c = Class.forName ("tuberload.phuzionbot.TestPlugin");
         TestPlugin pt = (TestPlugin)c.newInstance();
         
         pt.test();
      } catch (Exception exc) { System.out.println (exc); }
   }
}


TestPlugin.java:
package tuberload.phuzionbot;

public class TestPlugin
{
   public void test()
   {
      System.out.println ("Executing test plugin.");
   }
}


This is a very simple example but could be made into something much bigger. Once I finish developing my plugin system I will post it here.
Quote"Pray not for lighter burdens, but for stronger backs." -- Teddy Roosevelt
"Your forefathers have given you freedom, so good luck, see you around, hope you make it" -- Unknown

iago

That's the same thing I do:

   public static boolean runCommand(String username, String message, BNetWrapper out, boolean remote) throws java.io.IOException
   {
       if(message == null || message.length() < 2)
           return NOTHANDLED;

       char triggerChar = remote ? out.getSetting("trigger").charAt(0) : '/';

       if(message.charAt(0) != triggerChar)
           return NOTHANDLED;

       String commandName = message;
       String parameters = null;

       int space = message.indexOf(' ');

       if(space != -1)
       {
           commandName = message.substring(0, space);
           parameters = message.substring(space + 1);
       }

       commandName = Character.toUpperCase(commandName.charAt(1)) + (commandName.length() > 1 ? commandName.substring(2).toLowerCase()
: "");

       try
       {
           if(remote == CommandInterface.REMOTE)
           {
               // Verify the user has the appropriate flags to access this command
               RemoteCommandInterface command = (RemoteCommandInterface)Class.forName("javaop.bnet.commands.remotehandlers." + command
Name).newInstance();

               if(command.getRequiredFlags().length() == 0 || out.hasAny(username, command.getRequiredFlags()))
                   return command.execute(username, parameters, out);
               else
                   throw new AccessException(username, commandName);
           }
           else
               return ((CommandInterface)Class.forName("javaop.bnet.commands.handlers."       + commandName).newInstance()).execute(co
mmandName, parameters, out);
       }
       catch(AccessException e)
       {
           out.log("User attempted to access illegal command: " + e, 0);
           return HANDLED;
       }
       catch(UsageException e)
       {
           out.sendWhisper(username, e.toString());
           return HANDLED;
       }
       catch(Exception e)
       {
           e.printStackTrace();
           return NOTHANDLED;
       }
   }
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Tuberload

#2
Ah very nice. I am making it so the plugin manager registers the plugins to receive events from the event generator. That way they get direct access to whatever they want. Basically they will inherit an interface depending on what Battle.net packets they want to receive events for.

I am also working on making my own simple script engine for fun, but I figured why not use the power of Java as far as the core plugins are concerned.
Quote"Pray not for lighter burdens, but for stronger backs." -- Teddy Roosevelt
"Your forefathers have given you freedom, so good luck, see you around, hope you make it" -- Unknown

MyndFyre

Quote from: Tuberload on June 15, 2004, 05:18 PM
This is just a quick example to start off with demonstrating how to dynamically load and use Java class files.

PluginLoader.java:
package tuberload.phuzionbot;

public class PluginLoader
{
   public static void main (String[] args)
   {
      PluginLoader pl = new PluginLoader();
      
      pl.run();
   }
   
   public void run()
   {
      try
      {
         Class c = Class.forName ("tuberload.phuzionbot.TestPlugin");
         TestPlugin pt = (TestPlugin)c.newInstance();
         
         pt.test();
      } catch (Exception exc) { System.out.println (exc); }
   }
}


TestPlugin.java:
package tuberload.phuzionbot;

public class TestPlugin
{
   public void test()
   {
      System.out.println ("Executing test plugin.");
   }
}


This is a very simple example but could be made into something much bigger. Once I finish developing my plugin system I will post it here.

I'm ignoring iago's system cuz I'm too lazy to read it....

Anyway, I was just going to suggest you make the plugins either implement an interface or inherit from a class.  That way they all override base plugin functionality and you know what methods you can call on the descendent plugin.  You don't need to reflect over the methods or anything, and you can create and cast an instance of the object in your compiled class without knowing what the name of the descendent plugin class will be named.  :)

Just a thought, don't know if you were considering it.

For me, all plugin classes must inherit from PluginBase.  (They also must have a class-level attribute, but that's a C# thing).  The PluginBase class is defined as something like this:



public abstract class PluginBase
{
 public abstract void Initialize(IEventHost eventHost,
   StringDictionary persistedSettings);
 public abstract void Close(IEventHost eventHost);

 public abstract void PersistSettings(StringDictionary persistedSettings);
}



The Initialize method registers all event handlers the plugin will handle, and also loads the settings from a StringDictionary marshaled by the bot.  (A StringDictionary is a special type of collection that is essentially a typed Hashtable, that lets me use string indexers rather than generic Object or numeric indexers, to retrieve strings).  Close tells the plugin to unregister its event handlers, and PersistSettings tells the plugin to save its settings for the next round.

Anyway, just some suggestions.
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

iago

Myndfyre -- mine implements an interface like you suggested.  The RemoteCommand interface is pretty sexy:

public interface RemoteCommandInterface extends CommandInterface
{
   public String getName();
   public String getUsage();
   public String getRequiredFlags();
   public String getDescription();
}


The flags are automatically checked, so I don't have to worry as long as I specify which flags people need.  And when somebody does .help [command], it calls .getDescription.  I also wrote a program that steps through the directory and summarizes the commands, producing output like:
www.valhallalegends.com/iago/commands.txt
www.valhallalegends.com/iago/flags.txt
(I know those are inaccessable right now, but they will be ok later)
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Tuberload

#5
Quote from: Myndfyre on June 15, 2004, 07:44 PMI'm ignoring iago's system cuz I'm too lazy to read it....

Anyway, I was just going to suggest you make the plugins either implement an interface or inherit from a class.  That way they all override base plugin functionality and you know what methods you can call on the descendent plugin.  You don't need to reflect over the methods or anything, and you can create and cast an instance of the object in your compiled class without knowing what the name of the descendent plugin class will be named.  :)

Just a thought, don't know if you were considering it.

For me, all plugin classes must inherit from PluginBase.  (They also must have a class-level attribute, but that's a C# thing).  The PluginBase class is defined as something like this:

Did you ignore my second post as well:  :P

Quote from: Tuberload on June 15, 2004, 06:10 PM
Ah very nice. I am making it so the plugin manager registers the plugins to receive events from the event generator. That way they get direct access to whatever they want. Basically they will inherit an interface depending on what Battle.net packets they want to receive events for.

I am also working on making my own simple script engine for fun, but I figured why not use the power of Java as far as the core plugins are concerned.

Like the original post says, this is just a quick example of how to dynamically load a Java class and use it.

I have created multiple interfaces for plugin developers to inherit based upon their desired access to the bot. Your suggestions are welcome though; perhaps we can discuss this more when I release my working solution.
Quote"Pray not for lighter burdens, but for stronger backs." -- Teddy Roosevelt
"Your forefathers have given you freedom, so good luck, see you around, hope you make it" -- Unknown

MyndFyre

Quote from: Tuberload on June 15, 2004, 08:42 PM
Did you ignore my second post as well:  :P

Quote from: Tuberload on June 15, 2004, 06:10 PM
Ah very nice. I am making it so the plugin manager registers the plugins to receive events from the event generator. That way they get direct access to whatever they want. Basically they will inherit an interface depending on what Battle.net packets they want to receive events for.

I am also working on making my own simple script engine for fun, but I figured why not use the power of Java as far as the core plugins are concerned.

Like the original post says, this is just a quick example of how to dynamically load a Java class and use it.

I have created multiple interfaces for plugin developers to inherit based upon their desired access to the bot. Your suggestions are welcome though; perhaps we can discuss this more when I release my working solution.

No, Tuberload, I didn't ignore your second post.  However, neither post mentioned interfaces or parent classes.  Your code from the first post:

TestPlugin.java:
package tuberload.phuzionbot;

public class TestPlugin
{
  public void test()
  {
     System.out.println ("Executing test plugin.");
  }
}


TestPlugin doesn't extend or implement anything.  And your loader:


Class c = Class.forName ("tuberload.phuzionbot.TestPlugin");
TestPlugin pt = (TestPlugin)c.newInstance();


It doesn't truly "dynamically" load your plugin -- you provide the type name, which *could* dynamically load a plugin, if you made the parameter to .forName() a string variable rather than a literal.  HOWEVER, the example that you gave makes such loading irrelevant, because you then use code that knows the type -- TestPlugin -- that you're dynamically loading.

To make it work for any type, you'd need to either inherit a base class or implement interfaces (which I saw you said you did).  But then, what you'd want to do is, rather than making the plugin class name part of the code, make the base class/interface what you declare in code:

IPlugin plugin = (IPlugin)c.newInstance();


Anyway, I don't know if you made that clear (I don't think you did), and that's what my post was trying to get to.  Your example wasn't a quick example of how to dynamically load a Java class, because the next statement would have loaded it anyway, without the loading being dynamic.

My 2 cents.  (For those on this forum that think I'm Mr. C#, I have 1+ yr of Java experience.  I just choose not to talk about it too much :P )
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

Tuberload

#7
Quote from: Tuberload on June 15, 2004, 05:18 PM
This is just a quick example to start off with demonstrating how to dynamically load and use Java class files.

This is a very simple example but could be made into something much bigger. Once I finish developing my plugin system I will post it here.

Ah yes, that must not have been very clear that this was just an example of how to use Java Class objects... Why would anyone give a quick example of something, in an attempt to spark some thinking, when they could just wait until they were finished and give it all away? Beats me...

Quote from: Tuberload on June 15, 2004, 06:10 PM
Ah very nice. I am making it so the plugin manager registers the plugins to receive events from the event generator. That way they get direct access to whatever they want. Basically they will inherit an interface depending on what Battle.net packets they want to receive events for.

I am also working on making my own simple script engine for fun, but I figured why not use the power of Java as far as the core plugins are concerned.

Hrmmm, that looks like I talked about inheriting an interface to me...

To me it seems you just glanced at my source code, and hurried up to make a post on the *right* way to do things. (For those of you on the forum I have 2+ years of Java experience, so I do have somewhat of a clue about what is going on.)
Quote"Pray not for lighter burdens, but for stronger backs." -- Teddy Roosevelt
"Your forefathers have given you freedom, so good luck, see you around, hope you make it" -- Unknown