Skip to content

Instantly share code, notes, and snippets.

@nielsmh
Last active April 6, 2021 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nielsmh/8f7caa144f435189e9c392d267f73733 to your computer and use it in GitHub Desktop.
Save nielsmh/8f7caa144f435189e9c392d267f73733 to your computer and use it in GitHub Desktop.

Generic plugins for OpenTTD

Basically to support extensibility of the "Rich Presence" API and to not lock it in to the term "social", make a generic binary plugin system for OpenTTD.

It should have a simple per-OS loader that detects and loads dynamic libraries of all relevant plugins at startup, and then offers a system-agnostic way to query plugins for supporting a specific interface.

There may be situations where multiple plugins support the same interface, and sometimes it may be relevant to know either just one that supports something, or sometimes to know all plugins that support something.

API for plugin dynamic libraries

extern "C" void *OTTDPlugin(const char *interface_name);

As simple as that. The dynamic library exports a single function named OTTDPlugin. This function takes the name of an interface as a string, and if it recognizes the interface returns a pointer to a struct defining the interface.

The plugin and interfaces should not assume any kind of heavy initialisation should take place when an interface is requested. Rather, an interface that requires initialisation should have a call to do that. This initialisation can also be where the consumer side in OpenTTD registers any callbacks necessary.

Internal API for accessing plugins inside OpenTTD

/**
 * Detect all installed plugins and load them
 * Separate implementation for each OS supported.
 */
void OSLoadPlugins();

/**
 * Get an implementation for the named plugin interface.
 * Queries all loaded plugins for the named interface, in unspecified order,
 * and returns the first implemtation of the interface found.
 */
template<typename T>
T *PluginGetInterface(const char *interface_name);

/**
 * Get all implementations for the named plugin interface.
 * Queries all loaded plugins for the named interface, in unspecified order,
 * and returns a vector of all found implementations of the interface.
 */
template<typename T>
std::vector<T *> PluginGetAllInterface(const char *interface_name);

No function to unload plugins, or detect new ones after startup. Plugins are unloaded when the process exits.

Example for current social API prototype

The plugin would implement an interface named e.g. "SocialPresence_4" (version 4 of the SocialPresence API).

When the plugin is queried for this name via the exported function, it returns a struct of function pointers that could look like this.

/** Function pointers supplied by the plug-in for OpenTTD to call */
struct OpenTTD_SocialPluginApi_4 {
	/** OpenTTD calls this function when starting up the social presence system to register callbacks */
	void (*init)(struct OpenTTD_SocialPluginCallbacks_4 *callbacks);
	/** OpenTTD calls this function when it prepares to exit */
	void (*shutdown)();
	/** OpenTTD calls this function at regular intervals, where it is safe to call the callback functions */
	void (*event_loop)();
	/** OpenTTD calls this function when the player enters a singleplayer game */
	void (*enter_singleplayer)();
	/** OpenTTD calls this function when the player enters a multiplayer game */
	void (*enter_multiplayer)(const char *server_name, const char *server_cookie);
	/** OpenTTD calls this function when the player changes controlled company, or the company changes name */
	void (*enter_company)(const char *company_name, int company_id);
	/** OpenTTD calls this function when the player joins the spectators */
	void (*enter_spectate)();
	/** OpenTTD calls this function when the player leaves the main gameplay */
	void (*exit_gameplay)();
	/** OpenTTD calls this function when the player responds to a received join request */
	void (*respond_join_request)(void *join_request_cookie, OpenTTD_SocialPluginApi_JoinRequestResponse response);
	/** OpenTTD calls this function to display a web browser via integration */
	void (*show_web_browser)(const char *uri);
	/** OpenTTD calls this function to get the user's preferred player name */
	void (*get_preferred_player_name)(char *name_buffer, unsigned int name_buffer_size);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment