Plugin System
CraftOS-PC features a new plugin system that allows importing external Lua libraries into the ComputerCraft environment. Many pre-existing Lua libraries work out of the box, including some LuaRocks. A CCEmuX adapter plugin is avaiable with all binary versions of CraftOS-PC v2.2 and later.
Installing
Plugins can be installed in these locations depending on your platform:
- Windows:
C:\Program Files\CraftOS-PC\plugins - macOS:
CraftOS-PC.app/Contents/PlugIns - Linux:
/usr/share/craftos/plugins
The plugin file should have the name that the library's luaopen_ function uses. For example, the CCEmuX plugin should be named ccemux because the loader function is named luaopen_ccemux. For some plugins (such as Lua libraries), this is required, but most plugins designed for CraftOS-PC (like the CCEmuX plugin) will tell CraftOS-PC what name to use automatically. Once a plugin is installed it will be available as a global API with the same name as the plugin.
Using Lua libraries
CraftOS-PC can automatically import Lua libraries as they would be used with standard Lua. Even though CraftOS-PC uses its own version of Lua, you can still use libraries that were built for standard Lua 5.2.
To use a Lua library with CraftOS-PC, simply drop the DLL/dylib/so file into the plugins folder as listed above, and rename it so it starts with lua_ (e.g. lua_zlib.dll). This will tell CraftOS-PC not to search for plugin metadata, and instead treats it like a standard library. Make sure to keep the rest of the file name the same, as this is what CraftOS-PC uses to determine the library's name.
Writing plugins
A plugin consists of three main functions: plugin_init, luaopen, and plugin_deinit. plugin_init is called on the main thread when CraftOS-PC starts up, before any computers are started. It receives a pointer to a PluginFunctions structure (see below), and the path to the library as a path_t reference. It returns a pointer to a PluginInfo structure with the plugin's information filled in. This pointer can either be a static reference, or a dynamically allocated pointer (make sure to deallocate it in plugin_deinit, though).
The signature for plugin_init is:
PluginInfo * plugin_init(const PluginFunctions * func, const path_t& path);
luaopen is the main opener function that is used to initialize the API. It's called on the computer thread every time a computer boots up (this may be after a reboot!). It is a standard C Lua function, and is called in the same way as a normal Lua library opener. It takes one string - the API name - on the stack, and returns a value with which to set the API global to. The function should be named luaopen_<name>, where <name> is the name of the API. <name> is normally inferred from the plugin file's name, but if PluginInfo.apiName or PluginInfo.luaopenName is set, it will use that field instead.
plugin_deinit is called on the main thread before CraftOS-PC quits. It can also be called immediately after plugin_init if an error was returned (however, it's not called if an exception is thrown). It takes the pointer returned from plugin_init, and returns nothing. plugin_deinit is not required for a plugin to work, but it can be used if you need to free resources or to delete a dynamically allocated PluginInfo structure.
The signature for plugin_deinit is:
void plugin_deinit(PluginInfo * info);
All of these functions should be declared as exported C functions. This means they should be inside an extern "C" block, and on Windows have the __declspec(dllexport) qualifier. The CraftOS-PC header defines the DLLEXPORT macro to take care of __declspec for you. Examples of plugins, as well as some starter files, are contained inside the examples directory of the CraftOS-PC repository.
The main plugin structures are documented below. However, note that this information is not always up-to-date. It is recommended that you read through the header directly for the latest information.
PluginFunctions structure
The PluginFunctions structure is used to hold all of the functions that a plugin may use to interact with CraftOS-PC. This structure is passed (as a constant pointer) to the plugin_init function.
There are two different version fields in this structure. The first, abi_version, is used to determine the version of the ABI. Changes to this version indicate a fundamental incompatibility, and you should not continue loading if this version doesn't match the expected version.
The second field, structure_version, is used to determine what functions are exported in both the Computer and PluginFunctions structure. This version is bumped when new fields are added to one of the structure definitions. Check this field before using any fields added after structure version 0. It is not required that this field exactly match - as long as the version is greater than or equal to the minimum that your plugin requires to function, you may continue loading.
Do NOT rely on any non-version-0 fields to exist without checking the structure version. If you do this, users using your plugin on an old version of CraftOS-PC will likely experience a segmentation fault/crash when the plugin attempts to load the non-existing function. Instead, you should check that the structure version is compatible, and warn or error that the plugin is incompatible with the current version (without crashing).
Do note that if your plugin doesn't require any CraftOS-PC structures during initialization, you can let CraftOS-PC handle version checks by returning an info structure with the required versions filled in (see below). If the version numbers don't match, CraftOS-PC will stop loading your plugin. However, this will not suffice if you need to access the structures in your plugin_init.
You may see references to version numbers in the form xx.yy - this is a shorthand form to represent <abi_version>.<structure_version>.
unsigned abi_version
The plugin ABI version that is supported by this copy of CraftOS-PC. This version must exactly match your plugin's API version. You should check this version before doing anything else.
unsigned structure_version
The version of the PluginFunctions, Computer, and configuration structures. Check this version before using any field that isn't available in version 0. This version must be equal to or greater than your plugin's minimum structure version.
const int & selectedRenderer
A reference to the variable holding the current renderer.
const configuration * config
A pointer to the global configuration.
path_t getBasePath()
Returns the path to the CraftOS-PC data root.
Returns
The path to the CraftOS-PC data root.
path_t getROMPath()
Returns the path to the ROM.
Returns
The path to the ROM.
library_t * getLibrary(const std::string & name)
Returns the library structure for a built-in API.
Parameters
nameThe name of the API to get
Returns
A pointer to the library structure for the selected API
Computer * getComputerById(int id)
Returns the computer object for a specific ID.
Parameters
idThe ID of the computer to get
Returns
The computer object, or NULL if a computer with that ID isn't running
void registerPeripheral(const std::string & name,const peripheral_init & initializer)
Registers a peripheral with the specified name.
Parameters
nameThe name of the peripheral to register.initializerThe initialization function that creates the peripheral object.
See also: peripheral_init The prototype for a peripheral initializer
void registerSDLEvent(SDL_EventType type,const sdl_event_handler & handler,void * userdata)
Registers an SDL event hook to call a function when the specified event occurs.
Parameters
typeThe type of event to listen forhandlerThe function to call when the event occursuserdataAn opaque pointer to pass to the handler function
See also: sdl_event_handler The prototype for an event handler
bool addMount(Computer * comp,const path_t & real_path,const char * comp_path,bool read_only)
Adds a directory mount to a computer.
Parameters
compThe computer to mount onreal_pathThe path to the directory to mountcomp_pathThe path inside the computer to mount onread_onlyWhether the mount should be read-only
Returns
Whether the mount succeeded
bool addVirtualMount(Computer * comp,const FileEntry & vfs,const char * comp_path)
Adds a virtual mount to a computer.
Parameters
compThe computer to mount onvfsThe virtual filesystem file entry to mountcomp_pathThe path inside the computer to mount on
Returns
Whether the mount succeeded
Computer * startComputer(int id)
Starts up a computer with the specified ID.
Parameters
idThe ID of the computer to start
Returns
The Computer object for the new computer
void queueEvent(Computer * comp,const event_provider & event,void * userdata)
Queues a Lua event to be sent to a computer.
Parameters
compThe computer to send the event toeventThe event provider function to queueuserdataAn opaque pointer storing any user data for the provider
See also: event_provider The prototype for the event provider
void * queueTask(const std::function< void *(void *)> & func,void * userdata,bool async)
Runs a function on the main thread, and returns the result from the function.
Parameters
funcThe function to calluserdataAn opaque pointer to pass to the functionasyncWhether to run the function asynchronously (if true, returns NULL immediately)
Returns
The value returned from the function, or NULL if async is true
std::string getConfigSetting(const std::string & name)
Returns the value of a custom configuration setting as a string.
Parameters
nameThe name of the setting
Returns
The value of the setting
Exceptions
std::out_of_rangeIf the config setting does not exist
int getConfigSettingInt(const std::string & name)
Returns the value of a custom configuration setting as an integer.
Parameters
nameThe name of the setting
Returns
The value of the setting
Exceptions
std::out_of_rangeIf the config setting does not existstd::invalid_argumentIf the config setting is not an integer
bool getConfigSettingBool(const std::string & name)
Returns the value of a custom configuration setting as a boolean.
Parameters
nameThe name of the setting
Returns
The value of the setting
Exceptions
std::out_of_rangeIf the config setting does not existstd::invalid_argumentIf the config setting is not a boolean
void setConfigSetting(const std::string & name,const std::string & value)
Sets a custom configuration variable as a string.
Parameters
nameThe name of the settingvalueThe value of the setting
void setConfigSettingInt(const std::string & name,int value)
Sets a custom configuration variable as an integer.
Parameters
nameThe name of the settingvalueThe value of the setting
void setConfigSettingBool(const std::string & name,bool value)
Sets a custom configuration variable as a boolean.
Parameters
nameThe name of the settingvalueThe value of the setting
void registerConfigSetting(const std::string & name,int type,const std::function<int(const std::string &,void *)> & callback, void * userdata)
Registers a custom config setting so it can be accessed with the config API, with an optional callback. Pass nullptr to callback to ignore.
Parameters
nameThe name of the settingtypeThe type of the setting: 0 for boolean, 1 for integer, 2 for stringcallbackA callback to call when the setting is changed. This takes the name and userdata, and returns 0 for immediate use, 1 to reboot the computer, and 2 to restart CraftOS-PC before taking effect. Set this to nullptr to not call a function.userdataAn optional opaque pointer to pass to the function.
peripheral * attachPeripheral(Computer * computer, const std::string& side, const std::string& type, std::string * errorReturn, const char * format, ...)
Attaches a peripheral of the specified type to a side, with optional extended arguments.
Parameters
computerThe computer to attach tosideThe side to attach the peripheral ontypeThe type of peripheral to attacherrorReturnA pointer to a string to hold an error message (NULL to ignore)formatA format string specifying the arguments passed - 1 character per argument; set to "L" to pass a Lua state instead
'i' = lua_Integer, 'n' = lua_Number, 's' = const char *, 'b' = bool, 'N' = nil/NULL (pass NULL in the arg list)...Any arguments to pass to the constructor
Returns
The new peripheral object, or NULL on error
Throws
std::invalid_argumentIf the format string is invalidstd::exceptionIf the peripheral constructor throws an exception
bool detachPeripheral(Computer * computer, const std::string& side)
Detaches a peripheral from a side.
Parameters
computerThe computer to detach fromsideThe side to detach
Returns
Whether the operation succeeded
void addEventHook(const std::string& event, Computer * computer, const event_hook& hook, void* userdata)
Adds a hook function to be called when an event of a specific type is queued from C++. The hook is called directly after the callback function for the event, with the same parameters as an event provider + an additional field for the event name. It returns the new name of the event, which for most applications should be the same as the input. If the event name returned is empty, the event is removed from the queue. Hooks are executed in the order they were added. Computer hooks are executed before global hooks.
Parameters
eventThe name of the event to hookcomputerThe computer to hook for, or NULL for all computershookThe hook function to executeuserdataAn opaque pointer to pass to the function
void setDistanceProvider(const std::function<double(const Computer *, const Computer *)>& func)
Sets a custom disance provider for modems.
Parameters
funcThe callback function to use to get distance. It takes two computer arguments (the sender and receiver), and returns a double specifying the distance.
PluginInfo structure
The PluginInfo structure is used to hold information about a plugin. This structure is returned by plugin_init to indicate some properties about the plugin. The default values in this structure will not change any functionality - feel free to leave them at their default values, or change them to configure your plugin.
unsigned abi_version
The required ABI version for the plugin. Defaults to the version being built for.
unsigned minimum_structure_version
The minumum required structure version. Defaults to 0 (works with any version).
std::string luaopenName
The name of the luaopen function. This may be useful to be able to rename the plugin file without breaking luaopen.
std::string failureReason
This can be used to trigger a load failure without throwing an exception. Set this field to any non-blank value to stop loading.
std::string apiName
The name of the API. This can be used to override the default, which is determined by filename. This will also affect luaopenName if that's not set.
inline PluginInfo(const std::string & api,unsigned sv)
Returns a dynamically-allocated error info structure. Make sure you have a plugin_deinit function in place first.
Parameters
apiThe name of the API.svThe minimum structure version required for the plugin; defaults to 0.
ABI compatibility
When compiling plugins, you must use a compiler that is compatible with CraftOS-PC.
- On Windows, CraftOS-PC is built with Visual Studio 2019. You may build plugins for Windows with Visual Studio 2015, 2017, or 2019, or Clang. You may not use MinGW to build plugins.
- On macOS, CraftOS-PC is built with Clang 12 using libc++. You may build plugins for macOS with Clang. You may not use GCC to build plugins, unless you manually link with libc++.
- On Linux, CraftOS-PC is built with GCC using libstdc++. You may build plugins for Linux with GCC 3.4.0+, or Clang with libstdc++ (default, or use
-stdlib=libstdc++).
API versions
| API Version | Structure Version | CraftOS-PC Version |
|---|---|---|
| (none) | (none) | v2.0 - v2.1.3 |
| 2 | (none) | v2.2 - v2.3.4 |
| 3 | (none) | Accelerated v2.2 |
| 4 | (none) | v2.4 - v2.4.5 |
| 10 | 0 | v2.5 - v2.5.1.1 |
| 10 | 1 | v2.5.2 |
| 10 | 2 | v2.5.3 |
| 10 | 3 | v2.5.4 - v2.5.5 |
| 10 | 4 | v2.6 |
| 10 | 5 | v2.6.1 |
| 10 | 6 | v2.6.2 |
| 10 | 7 | v2.6.3 - v2.6.4 |
| 10 | 8 | v2.6.5 - v2.7.5 |
| 12 | 0 | v2.8 |
| 11 | 0 | Accelerated v2.5 - v2.5.1.1 |
| 11 | 1 | Accelerated v2.5.2 |
| 11 | 2 | Accelerated v2.5.3 |
| 11 | 3 | Accelerated v2.5.4 - v2.5.5 |
| 11 | 4 | Accelerated v2.6 |
| 11 | 5 | Accelerated v2.6.1 |
| 11 | 6 | Accelerated v2.6.2 |
| 11 | 7 | Accelerated v2.6.3 - v2.6.4 |
| 11 | 8 | Accelerated v2.6.5 - v2.7.5 |
Note: When building for debug targets on Windows, add 100000 to the plugin version.