Introduction

The plugin architecture for Doomseeker has been designed to minimize the upfront work. A valid plugin must provide some simple initialization structures as well as implement the protocol for server querying. A master server protocol is not required.

Once a plugin is registered the user can register servers manually through the standard configuration dialog or query a list from the master provided the plugin supports this. Doomseeker will request a query packet from the plugin and feed any responses from the server's ip and port back to the plugin.

Unfortunately it would be difficult for us to release a standalone development kit for the plugins, so you will need to either grab the latest source distribution or fork our Git repository.

Setting up a New Plugin

To start, create a new directory in the src/plugins directory for your plugin. In there you will need the following files: CMakeLists.txt, engineplugin.cpp, engineplugin.h, server.cpp, and server.h

The names of the last four files are of course up to you to decide (in fact I encourage you to use different names), but I'll be referring to them as stated above for the purposes of this tutorial. For the CMakeLists.txt file you can use the following template to get started.

cmake_minimum_required(VERSION 2.4)

set(PLUGIN_NAME myplugin)
include(../PluginHeader.txt)

set(MYPLUGIN_QT_FILES
	server.h
	...other h files with Qt code...
)
set(MYPLUGIN_FILES
	engineplugin.cpp
	server.cpp
	...other cpp files...
)
set(HEADER_FILES
	${MYPLUGIN_QT_FILES}
	engineplugin.h
	server.h
	...other h files...
)

qt_wrap_cpp(myPlugin MYPLUGIN_FILES ${MYPLUGIN_QT_FILES})

add_library(${PLUGIN_NAME} MODULE ${MYPLUGIN_FILES} ${HEADER_FILES})
target_link_libraries(${PLUGIN_NAME} ${PLUGIN_LIBS})

include(../PluginFooter.txt)

If you do not have any files which need to be wrapped by Qt, you can of course leave out the QT_FILES variable and the qt_wrap_cpp command. The HEADER_FILES variable mainly used when generating project files for various IDEs.

Further instructions on how to add your plugin to the build system will be given at the end of the tutorial in the "Compiling" section.

Defining the Engine Plugin

Next we will create our EnginePlugin class. In engineplugin.h, use the following template to get started:

#ifndef __MY_ENGINEPLUGIN__
#define __MY_ENGINEPLUGIN__

#include "plugins/engineplugin.h"

class MyEnginePlugin : public EnginePlugin
{
	DECLARE_PLUGIN(MyEnginePlugin) // Must be the same as class name
	public:
		MyEnginePlugin();
		Server *server(const QHostAddress &address, unsigned short port) const;
};

#endif

For this class the constructor will be used to tell Doomseeker what features your engine supports as well as other data such as any IRC channels. The server function will create a instance of the server class for us to query through. We'll be defining our Server class later. For now lets look at the implementation of these in engineplugin.cpp:

#include "plugins/engineplugin.h"

#include "engineplugin.h"
#include "server.h"

INSTALL_PLUGIN(MyEnginePlugin) // Must be the EnginePlugin class name

MyEnginePlugin::MyEnginePlugin()
{
	// You can import your engine's icon here:
	const // clear warnings
	#include "myengine.xpm"

	init("My Engine", myengine_xpm,
		EP_Author, "Your name here",
		EP_Version, 1,

		EP_Done
	);
}

Server *MyEnginePlugin::server(const QHostAddress &address, unsigned short port) const
{
	return new MyServer(address, port);
}

Defining a Server Protocol

The following template provides the minimum needed to implement a Server class:

#ifndef __MYSERVER_H__
#define __MYSERVER_H__

#include "serverapi/server.h"

class MyServer : public Server
{
	Q_OBJECT

	public:
		MyServer(const QHostAddress &address, unsigned short port);

		const EnginePlugin *plugin() const;

	protected:
		Response readRequest(QByteArray &data);
		bool sendRequest(QByteArray &data);
};

#endif

The general flow of execution will be for Doomseeker to call sendRequest with an array for you to fill. Packets recieved with your server's address and port will be forwarded through readRequest.

The plugin() function will return a pointer to the staticInstance() of your EnginePlugin. This function is provided by the macros in the API, so this probably always be as defined in the template below:

#include "server.h"
#include "engineplugin.h"

MyServer::MyServer(const QHostAddress &address, unsigned short port) : Server(address, port)
{
}

const EnginePlugin *MyServer::plugin() const
{
	// This function is provided automatically by the API macros.
	return MyEnginePlugin::staticInstance();
}

Server::Response MyServer::readRequest(QByteArray &data)
{
	return RESPONSE_GOOD;
}

bool MyServer::sendRequest(QByteArray &data)
{
	return true;
}

Compiling

We have now implemented a basic plugin which does nothing but implement the required portion of the plugin interface. To compile open your src/plugins/CMakeLists.txt file and add the name of your plugin's directory to the list there as an add_subdirectory command. Finally, build Doomseeker and your plugin should be built as well.