Plugin Creation

From Pokémon Online Wiki
Jump to: navigation, search

Plugins allow you to extend the functionalities of the PO server or the PO client.

Currently "official" plugins include:

Server-Side:

  • Usage Statistics plugin
  • Battle Logs plugin

Client-Side:

  • QR Code plugin

The support for plugins is currently limited, it's extended as the need grows. So if you want to access some data in your plugin, don't hesitate to write a request.

Contents

[edit] Outline

A plugin is a shared library. You provide a function createPluginClass in your shared library, which returns a pointer of an instantiated class extending ServerPlugin for a server plugin, or extending ClientPlugin for a client plugin. Those abstract classes are available in the plugininterface.h header of the Server/TeamBuilder project.

For server plugins, a ServerInterface argument is passed to createPluginClass, which is a base class of the Server class, used to access server data through virtual functions. For client plugins, a MainEngineInterface argument is given, the base class of the MainEngine class. You can check their definitions in serverinterface.h and engineinterface.h.

So here is what the utmost basic functionalities are:

- inherit pluginName(), to return the name of the plugin as a QString.

And if you want your plugin to have a configuration widget:

- inherit hasConfigurationWidget() to return true - inherit getConfigurationWidget() to return the configuration widget

This can be seen in the code for the client plugin baseclass:

class ClientPlugin
{
public:
    /* The name of the option the plugin would take in the menu bar.
       Also appears as the name of the plugin */
    virtual QString pluginName() const = 0;
 
    /* A widget that would be used to configure the plugin in the menu bar.
       Return NULL if you don't want one (default behavior) */
    virtual QWidget * getConfigurationWidget() {
        return NULL;
    }
 
    virtual bool hasConfigurationWidget() const {
        return false;
    }
};

[edit] Example: The QR Code plugin

What the QRCode plugin does is simple:

  • it stores a pointer to the mainengineinterface argument given during its creation
  • it overrides hasConfigurationWidget to return true
  • when asked for the widget with getConfigurationWidget(), it converts the current team to a xml, zip it, and display the zipped data in a QR code, which is in a QLabel in the configuration widget

Basically, client side, you can even do a tetris or a snake as a plugin: just give a widget which manages the game with getConfigurationWidget(), and there you go. As such, you could do a plugin to download themes or music, or boxes, or other things very easily.

[edit] Server plugins

class ServerPlugin
{
public:
    /* The name of the option the plugin would take in the menu bar.
       Also appears as the name of the plugin */
    virtual QString pluginName() const = 0;
 
    /* A widget that would be used to configure the plugin in the menu bar.
       Return NULL if you don't want one (default behavior) */
    virtual QWidget * getConfigurationWidget() {
        return NULL;
    }
 
    virtual bool hasConfigurationWidget() const {
        return false;
    }
 
    virtual bool isReadyForDeletion() const {
        return true;
    }
 
    virtual BattlePlugin * getBattlePlugin (BattleInterface *) {
        return NULL;
    }
};

Server plugins are a bit more tricky than client plugins. They offer functionality to peek in battles, for example.

As such two more functions can be inherited, in ServerPlugin:

- isReadyForDeletion()

Return true if your plugin is ready to be deleted, i.e. it has processed all necessary data. The reason for this function is because if your plugin deals with battles for example, it may have created instances of battle plugins not yet deleted, and unloading the library while those battle plugins are still active would be catastrophic.

If you return false, your plugin will still be deactivated for any new battles, it will just be left with time to process any remaining stuff, and isReadyForDeletion() will be called repeatedly until you finally return true, at some time interval.

- getBattlePlugin(BattleInterface*)

Return a battle plugin if you wish to handle this battle, or false otherwise. If you return a battle plugin, the battle plugin will attach itself to the battle, until it detaches itself or the battle ends. This leads to:

[edit] Battle plugins

class BattlePlugin
{
public:
    typedef int (BattlePlugin::*Hook) ();
    virtual ~BattlePlugin(){}
 
    virtual QHash<QString, Hook> getHooks(){
        return QHash<QString, Hook>();
    }
};

Basically, when a BattlePlugin is provided to a battle, it gives a list of hooks that the battle can call. (Also, don't forget a BattleInterface* pointer was given when requesting for the BattlePlugin, so if you want to be able to access the battle data, you should keep that pointer and store it in your class).

Hooks are functions that return an int, which is 0 if it was ok, or -1 if the plugin has no further purposes and can be deleted. For example, for the usage stats plugin, as soon as it gets the data it needs, the battle plugin instance returns -1 in its hook and is deleted.

Attention: Hooks can have arbitrary arguments. They are converted to BattlePlugin::Hook when passed in order to have a simple way of passing them, but their argument specification, and more importantly, the hook list, is available in battlepluginstruct.cpp: as of now there are two hooks, "battleStarting(BattleInterface&)" and "emitCommand(BattleInterface&,int,int,QByteArray)". The arguments are specified in the hook key, so that if the format change and the battle plugin is not updated, invalid hooks will just be ignored.

Here is the hook reference:

  • battleStarting(BattleInterface&): Called once players have organized their teams and the battle is started. If you want to collect usage statistics, that is the good time
  • emitCommand(BattleInterface&,int,int,QByteArray): Called every time the battle sends a network command.
    • int slot: In case the second argument (players) is AllButPlayer (aka -2), the command is sent to every player except slot
    • int players: The players to send the command to. AllButPlayer (-2), All(-1), or the player (Player1 or Player2).
    • QByteArray data: The data of the command, in binary format.

Of course the hooks list can be improved, but with the BattleManager upcoming integration, a lot more stuff will be possible with just the emitCommand hook.

Personal tools
Namespaces

Variants
Actions
Navigation
Pokémon
Community
Toolbox
Helping out