B3 can be extended with plugins which allow you to customize B3 in two ways :
Each B3 plugin must have a unique name. This name is important as it will determine the name of the plugin python file and also the name of the plugin class.
Let's say you want to create the 'Hello world' plugin, then
The naming rules are :
All B3 plugins are licensed under the GNU General Public License 2 or later. See: http://www.bigbrotherbot.net/forums/plugin-developers/b3-and-the-gnu-general-public-license/
Therefor your plugin should start with this:
# # HelloWorld Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net) # Copyright (C) 2010 <Your Name Here> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Changelog: #
__version__ = '0.0.0' __author__ = 'Your name here' import b3 import b3.events import b3.plugin #--------------------------------------------------------------------------------------------------
All plugins inherit the original B3 Plugin class. This way all dirty work is already dealt with in our core. 1)
class HelloworldPlugin(b3.plugin.Plugin): requiresConfigFile = <False/True>
requiresConfigFile property to False. That way B3 won't fail if users do not associate a config file to your plugin in their b3.xml main config
This is where you read the different settings your users put in the plugin config file.
The code in the onLoadConfig is executed on two occasions :
onLoadConfig!reconfig command
If your plugin does not come with a config file, then you can just ignore the onLoadConfig method, else it will look like :
def onLoadConfig(self): # load our settings self.verbose('Loading config')
Every plugin has a onStartup method, where the events for the plugin and the used commands are registered for the plugin. On what game events should the plugin react and/or what commands can be issued by users.
def onStartup(self): """\ Initialize plugin settings """ # get the admin plugin so we can register commands self._adminPlugin = self.console.getPlugin('admin') if not self._adminPlugin: # something is wrong, can't start without admin plugin self.error('Could not find admin plugin') return False # Register commands self._adminPlugin.registerCommand(self, 'helloworld', 1, self.cmd_helloWorld, 'hello') # Register our events self.verbose('Registering events') self.registerEvent(b3.events.EVT_CLIENT_AUTH) self.debug('Started')
Catch the game events in the onEvent function here. This is where all 'registered' events for the plugin pass. 'registered' because you have to make sure the event is registered in the startup function. Otherwise the event will not pass along here.
def onEvent(self, event): """\ Handle intercepted events """ if event.type == b3.events.EVT_CLIENT_AUTH: helloWorld(event.client) pass else: self.dumpEvent(event)
You can see in the paragraph above that in this example the helloWorld function is executed when a player is authenticated by B3, on authentication it will say 'Hello World!' to the authed client.
The second function is a command definition. It is executed by the command !helloworld as registered in the startup function. Here you will not have to worry about levels, the registration of the command took care of that already.
def helloWorld(self, client): """\ Say 'Hello World!' to a client """ client.say('Hello World!') return def cmd_helloWorld(self, data, client, cmd=None): """\ - Say 'Hello World!' to yourself """ self.helloWorld(client) return
The complete plugin then would look like this:
# # HelloWorld Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net) # Copyright (C) 2010 <Your Name Here> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Changelog: # __version__ = '0.0.0' __author__ = 'Your name here' import b3 import b3.events import b3.plugin #-------------------------------------------------------------------------------------------------- class HelloworldPlugin(b3.plugin.Plugin): def onStartup(self): """\ Initialize plugin settings """ # get the admin plugin so we can register commands self._adminPlugin = self.console.getPlugin('admin') if not self._adminPlugin: # something is wrong, can't start without admin plugin self.error('Could not find admin plugin') return False # Register commands self._adminPlugin.registerCommand(self, 'helloworld', 1, self.cmd_helloWorld, 'hello') # Register our events self.verbose('Registering events') self.registerEvent(b3.events.EVT_CLIENT_AUTH) self.debug('Started') def onLoadConfig(self): # load our settings self.verbose('Loading config') def onEvent(self, event): """\ Handle intercepted events """ if event.type == b3.events.EVT_CLIENT_AUTH: helloWorld(event.client) pass else: self.dumpEvent(event) def helloWorld(self, client): """\ Say 'Hello World!' to a client """ client.say('Hello World!') return def cmd_helloWorld(self, data, client, cmd=None): """\ - Say 'Hello World!' to yourself """ self.helloWorld(client) return
When your plugin requires its own config file you have to supply one in the proper xml format.
If your plugin supports several games and default settings differ per game it is advised to provide multiple config files. Make sure you name the config files clearly like this:
helloworld-homefront.xml
helloworld-cod4.xml
helloworld-bfbc2.xml
so it is clear to all users what config settings are preferred per game.
Does your plugin require database tables for storage? Add an .sql file containing the MySQL instructions to generate the proper tables. Be careful with that .sql code, always make sure that already installed tables remain intact when the user decides to run the .sql code more than once. Also our setup procedure may run the code on re-installation.
So always create tables using the IF NOT EXIST addition. A good example is in the b3.sql file in the core package:
CREATE TABLE IF NOT EXISTS aliases ( id int(10) UNSIGNED NOT NULL AUTO_INCREMENT, num_used int(10) UNSIGNED NOT NULL DEFAULT '0', alias varchar(32) NOT NULL DEFAULT '', client_id int(10) UNSIGNED NOT NULL DEFAULT '0', time_add int(10) UNSIGNED NOT NULL DEFAULT '0', time_edit int(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (id), UNIQUE KEY alias (alias,client_id), KEY client_id (client_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Your plugin is ready for distribution. In order to get a general package format your plugin should be packaged in .zip format according to the following structure:
helloworld.zip (bold are folders inside the .zip file):
extplugins » helloworld.py
extplugins » conf » helloworld.xml
sql » helloworld.sql
readme.txt