# Example plugin structure my_plugin.plugin.tar |- plugin.toml |- signature.dat |- foo.wasm |- bar.wasm ## `plugin.toml` - Describes the layout of the plugin: what wasm files are needed, dependencies, past version hashes, etc. ``` # Name of plugin (not meaningful, just for humans to read) name = "my_plugin" # First is most recent past_versions = [ "ab45929002fb1223", "5f29b4c179fd778f", ] dependencies = [ "8ff7b56c8f9aa55d", # `my_other_plugin` ] ``` ## `signature.dat` - Contains the PGP signature of the author, signing the contents of the plugin (excluding `signature.dat`, for obvious reasons: a signature cannot sign itself) - Optional: you can enable an `allow_unsigned_plugins` flag in settings for the purpose of plugin development that allows plugins without this file # Local storage - Plugins can store data both server-side and client-side - Local storage is addressed server-side by `hash(server_uuid, plugin_hash)` - Local storage is addressed client-side by `hash(server_uuid, player_uuid, plugin_hash)` - This unique addressing prevents plugins accessing data that isn't relevant to the current game context. Plugins can't see the state of other plugins, state from other server sessions, or from other accounts. # IPC (Inter Plugin Communication) - Plugins can register channels with a name - Other plugins can access them using the plugin hash or the plugin name - An `IpcRequest` contains data up to 1024Ko - An `IpcResponse` can contain data up to 1024Ko - The `IpcResponse` is an enum: ```rust enum IpcResponse { Ok(Vec<u8>), // The data is usually encoded using serde and bincode. Each plugin can choose which encoding he wants. Forbidden(String /* cause */), NotFound /* If the hook wasn't found*/, InternalError(String /* Error string*/) /* If the hook failed to be handled (The target plugin crashes)*/, Empty, } ``` - The `Request` struct contains the following ```rust struct IpcRequest { hook_name: String, // equivalent of URL in HTTP distant_plugin_hash: String, // The hash of the plugin which sent a IPC distant_plugin_name: String, // The name of the plugin which sent a IPC data: Vec<u8> // The data is usually encoded using serde and bincode. Each plugin can choose which encoding he wants. } ``` - How to register an IPC ```rust #[derive(Serialize, Deserialize)] struct PermissionRequest(Uid, String) #[ipc] fn get_permission(request: &IpcRequest, plugin_state: &mut PluginState) -> IpcResponse{ if let Some(PermissionRequest(uid, perm)) = request.deserialize() { IpcResponse::serialize(plugin_state .get(uid) .map(|x| x.has_permission(perm)) .unwrap_or(false)) } else { IpcResponse::InternalError("Invalid data sent".to_string()) } } ```